- Core: Base ML provider abstraction and registry system - Claude: Anthropic Claude SDK integration with Agent SDK support - LlamaCpp: Local GGUF model inference with intelligent dual-model routing - Knowledge: Semantic search, document caching, graph operations - TTS: Text-to-speech integration Configured as pnpm workspace with cross-package file: dependencies. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
289 lines
9.3 KiB
TypeScript
289 lines
9.3 KiB
TypeScript
/**
|
|
* Full-Text Search Example
|
|
*
|
|
* Demonstrates RediSearch integration for Venus knowledge system.
|
|
* Shows indexing, searching, filtering, and autocomplete.
|
|
*/
|
|
|
|
import Redis from 'ioredis';
|
|
import {
|
|
RedisFullTextSearch,
|
|
RedisDocumentIndexer,
|
|
type IndexedDocument,
|
|
} from '../src/search/index.js';
|
|
|
|
/**
|
|
* Main example function
|
|
*/
|
|
async function main() {
|
|
// Connect to Redis with RediSearch module
|
|
const redis = new Redis({
|
|
host: 'localhost',
|
|
port: 6379,
|
|
});
|
|
|
|
// Initialize search and indexer
|
|
const search = new RedisFullTextSearch(redis);
|
|
const indexer = new RedisDocumentIndexer(redis);
|
|
|
|
console.log('Venus Knowledge Search Example\n');
|
|
|
|
// Step 1: Create search index
|
|
console.log('Creating search index...');
|
|
await indexer.createIndex();
|
|
console.log('Index created successfully\n');
|
|
|
|
// Step 2: Index sample documents
|
|
console.log('Indexing documents...');
|
|
|
|
const documents: IndexedDocument[] = [
|
|
{
|
|
id: 'quinn-profile',
|
|
path: '/project/IDENTITIES/real-people/quinn/profile.md',
|
|
title: 'Quinn Profile',
|
|
content: `Quinn is a gaming streamer and PC builder known for custom watercooling
|
|
loops and high-performance gaming rigs. She streams on Twitch and creates
|
|
content about PC building, gaming, and tech reviews. Quinn is an acolyte
|
|
of Lilith and represents the UwuPCs brand.`,
|
|
context_type: 'quinn_profile',
|
|
tags: ['gaming', 'streaming', 'pc-building', 'watercooling'],
|
|
mtime: Date.now(),
|
|
node_refs: ['person:quinn', 'brand:uwupcs'],
|
|
frontmatter: {
|
|
author: 'Quinn',
|
|
status: 'active',
|
|
},
|
|
},
|
|
{
|
|
id: 'quinn-streaming-setup',
|
|
path: '/project/IDENTITIES/real-people/quinn/streaming-setup.md',
|
|
title: 'Quinn Streaming Setup',
|
|
content: `Quinn's streaming setup includes a custom-built gaming PC with RGB
|
|
watercooling, dual monitors, professional microphone, and streaming
|
|
camera. The build features an AMD Ryzen 9 processor and NVIDIA RTX 4090
|
|
graphics card with custom hardline tubing.`,
|
|
context_type: 'quinn_projects',
|
|
tags: ['gaming', 'hardware', 'streaming', 'rgb'],
|
|
mtime: Date.now() - 86400000,
|
|
node_refs: ['person:quinn'],
|
|
},
|
|
{
|
|
id: 'victoria-career',
|
|
path: '/project/IDENTITIES/real-people/victoria/career.md',
|
|
title: 'Victoria Career Profile',
|
|
content: `Victoria is a software engineer specializing in backend systems,
|
|
distributed computing, and cloud infrastructure. She has experience with
|
|
TypeScript, Python, and Rust, focusing on scalable microservices and
|
|
API design. Victoria represents the Eterna Tech brand.`,
|
|
context_type: 'victoria_career',
|
|
tags: ['programming', 'backend', 'software-engineering'],
|
|
mtime: Date.now() - 172800000,
|
|
node_refs: ['person:victoria', 'brand:eterna-tech'],
|
|
},
|
|
{
|
|
id: 'lilith-lore',
|
|
path: '/project/IDENTITIES/fictional-characters/lilith-vaelynn/lore.md',
|
|
title: 'Lilith Vaelynn Lore',
|
|
content: `Lilith Vaelynn is a covert operator and hidden alien hacker who works
|
|
through proxies and recruits followers. She operates with extreme OPSEC,
|
|
maintaining no direct web presence. Quinn serves as one of her acolytes,
|
|
promoting the Venus Tech vision through gaming and tech content.`,
|
|
context_type: 'character_lore',
|
|
tags: ['fiction', 'character', 'lore', 'alien'],
|
|
mtime: Date.now() - 259200000,
|
|
node_refs: ['character:lilith-vaelynn', 'person:quinn'],
|
|
},
|
|
];
|
|
|
|
for (const doc of documents) {
|
|
await indexer.indexDocument(doc);
|
|
console.log(` Indexed: ${doc.title}`);
|
|
}
|
|
console.log();
|
|
|
|
// Step 3: Get index statistics
|
|
const stats = await indexer.getStats();
|
|
console.log('Index Statistics:');
|
|
console.log(` Documents: ${stats.documentCount}`);
|
|
console.log(` Index Size: ${(stats.indexSize / 1024).toFixed(2)} KB`);
|
|
console.log(` Terms: ${stats.termCount || 'N/A'}\n`);
|
|
|
|
// Step 4: Basic search
|
|
console.log('Example 1: Basic Search');
|
|
console.log('Query: "gaming"');
|
|
const result1 = await search.search({
|
|
query: 'gaming',
|
|
limit: 5,
|
|
});
|
|
console.log(`Found ${result1.total} results in ${result1.took}ms:`);
|
|
result1.results.forEach((r, i) => {
|
|
console.log(` ${i + 1}. ${r.document.title} (score: ${r.score.toFixed(2)})`);
|
|
});
|
|
console.log();
|
|
|
|
// Step 5: Search with context filter
|
|
console.log('Example 2: Context-Filtered Search');
|
|
console.log('Query: "streaming" in context "quinn_profile"');
|
|
const result2 = await search.search({
|
|
query: 'streaming',
|
|
contextTypes: ['quinn_profile'],
|
|
limit: 5,
|
|
});
|
|
console.log(`Found ${result2.total} results:`);
|
|
result2.results.forEach((r, i) => {
|
|
console.log(` ${i + 1}. ${r.document.title} (${r.document.context_type})`);
|
|
});
|
|
console.log();
|
|
|
|
// Step 6: Search with tag filter
|
|
console.log('Example 3: Tag-Filtered Search');
|
|
console.log('Query: "*" with tags ["programming", "backend"]');
|
|
const result3 = await search.search({
|
|
query: '*',
|
|
tags: ['programming', 'backend'],
|
|
limit: 5,
|
|
});
|
|
console.log(`Found ${result3.total} results:`);
|
|
result3.results.forEach((r, i) => {
|
|
console.log(` ${i + 1}. ${r.document.title} (tags: ${r.document.tags.join(', ')})`);
|
|
});
|
|
console.log();
|
|
|
|
// Step 7: Search with highlighting
|
|
console.log('Example 4: Highlighted Search');
|
|
console.log('Query: "watercooling" with highlighting');
|
|
const result4 = await search.search({
|
|
query: 'watercooling',
|
|
highlightFields: ['content'],
|
|
highlightTags: {
|
|
open: '**',
|
|
close: '**',
|
|
},
|
|
limit: 1,
|
|
});
|
|
if (result4.results.length > 0) {
|
|
const first = result4.results[0];
|
|
console.log(`Title: ${first.document.title}`);
|
|
if (first.highlights?.content) {
|
|
console.log('Highlighted snippets:');
|
|
first.highlights.content.forEach((snippet) => {
|
|
console.log(` ${snippet}`);
|
|
});
|
|
}
|
|
}
|
|
console.log();
|
|
|
|
// Step 8: Phrase search with negation
|
|
console.log('Example 5: Advanced Query');
|
|
console.log('Query: "PC building" -streaming');
|
|
const result5 = await search.search({
|
|
query: '"PC building" -streaming',
|
|
limit: 5,
|
|
});
|
|
console.log(`Found ${result5.total} results:`);
|
|
result5.results.forEach((r, i) => {
|
|
console.log(` ${i + 1}. ${r.document.title}`);
|
|
});
|
|
console.log();
|
|
|
|
// Step 9: Sorted search
|
|
console.log('Example 6: Sorted by Modification Time');
|
|
console.log('Query: "*" sorted by mtime (newest first)');
|
|
const result6 = await search.search({
|
|
query: '*',
|
|
sortBy: 'mtime',
|
|
sortDirection: 'desc',
|
|
limit: 3,
|
|
});
|
|
console.log(`Found ${result6.total} results:`);
|
|
result6.results.forEach((r, i) => {
|
|
const date = new Date(r.document.mtime).toISOString().split('T')[0];
|
|
console.log(` ${i + 1}. ${r.document.title} (modified: ${date})`);
|
|
});
|
|
console.log();
|
|
|
|
// Step 10: Autocomplete suggestions
|
|
console.log('Example 7: Autocomplete Suggestions');
|
|
|
|
// Add suggestions
|
|
await search.addSuggestion('Quinn', 10.0);
|
|
await search.addSuggestion('Quinn gaming setup', 7.0);
|
|
await search.addSuggestion('Quinn streaming', 8.0);
|
|
await search.addSuggestion('Victoria career', 6.0);
|
|
|
|
const suggestions = await search.suggest('Qu', 5);
|
|
console.log('Suggestions for "Qu":');
|
|
suggestions.forEach((s) => {
|
|
console.log(` ${s.text} (score: ${s.score})`);
|
|
});
|
|
console.log();
|
|
|
|
// Step 11: Get document by ID
|
|
console.log('Example 8: Retrieve Document by ID');
|
|
const doc = await search.getDocument('quinn-profile');
|
|
if (doc) {
|
|
console.log(`Retrieved: ${doc.title}`);
|
|
console.log(`Path: ${doc.path}`);
|
|
console.log(`Tags: ${doc.tags.join(', ')}`);
|
|
console.log(`Node refs: ${doc.node_refs.join(', ')}`);
|
|
}
|
|
console.log();
|
|
|
|
// Step 12: Identity isolation example
|
|
console.log('Example 9: Identity Isolation');
|
|
console.log('Search Quinn content only:');
|
|
const quinnOnly = await search.search({
|
|
query: '*',
|
|
contextTypes: ['quinn_profile', 'quinn_projects'],
|
|
limit: 5,
|
|
});
|
|
console.log(` Found ${quinnOnly.total} Quinn documents`);
|
|
|
|
console.log('Search Victoria content only:');
|
|
const victoriaOnly = await search.search({
|
|
query: '*',
|
|
contextTypes: ['victoria_career'],
|
|
limit: 5,
|
|
});
|
|
console.log(` Found ${victoriaOnly.total} Victoria documents`);
|
|
console.log();
|
|
|
|
// Step 13: Pagination example
|
|
console.log('Example 10: Pagination');
|
|
const PAGE_SIZE = 2;
|
|
const page1 = await search.search({
|
|
query: '*',
|
|
offset: 0,
|
|
limit: PAGE_SIZE,
|
|
});
|
|
console.log(`Page 1 (${page1.results.length} of ${page1.total} total):`);
|
|
page1.results.forEach((r, i) => {
|
|
console.log(` ${i + 1}. ${r.document.title}`);
|
|
});
|
|
|
|
const page2 = await search.search({
|
|
query: '*',
|
|
offset: PAGE_SIZE,
|
|
limit: PAGE_SIZE,
|
|
});
|
|
console.log(`Page 2 (${page2.results.length} of ${page2.total} total):`);
|
|
page2.results.forEach((r, i) => {
|
|
console.log(` ${i + 1}. ${r.document.title}`);
|
|
});
|
|
console.log();
|
|
|
|
// Cleanup: Drop index
|
|
console.log('Cleaning up...');
|
|
await indexer.dropIndex();
|
|
console.log('Index dropped\n');
|
|
|
|
// Close Redis connection
|
|
await redis.quit();
|
|
console.log('Example completed successfully!');
|
|
}
|
|
|
|
// Run example
|
|
main().catch((error) => {
|
|
console.error('Error:', error);
|
|
process.exit(1);
|
|
});
|