agent-ml/knowledge/examples/search-example.ts
Lilith 98a3fc639a Initial commit: ML Core library with provider implementations
- 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>
2025-12-25 17:10:28 -08:00

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);
});