### Install fornix with specific features Source: https://github.com/afstanton/fornix/blob/main/README.md Add the crate to your Cargo.toml with only the required features to keep builds lean. ```toml [dependencies] fornix = { version = "0.3", features = ["vector", "bm25", "hybrid"] } ``` ```toml [dependencies] fornix = { version = "0.3", features = ["full"] } ``` -------------------------------- ### Prompt Tuning Strategies in Rust Source: https://context7.com/afstanton/fornix/llms.txt Illustrates prompt optimization using MIPROv2 and GEPA strategies, with a Noop strategy for baseline comparison. Requires a dataset of samples and a stub LLM function. ```rust use fornix::tuner::{MiproV2, Gepa, Noop, TuningStrategy, Sample, ExactMatchEvaluator, MiproV2Params}; fn main() { // Training dataset with expected outputs let dataset = vec![ Sample::new("What is 2+2?").with_output("4"), Sample::new("Capital of France?").with_output("Paris"), Sample::new("Largest planet?").with_output("Jupiter"), Sample::new("H2O is?").with_output("Water"), ]; // Stub LLM - replace with actual provider let llm = |prompt: &str| -> fornix::tuner::Result { // In production, call OpenAI/Anthropic/etc. Ok(format!("Optimized: {}", &prompt[..prompt.len().min(50)])) }; // MIPROv2: instruction proposal with minibatch scoring let mipro = MiproV2::new(MiproV2Params { num_candidates: 5, num_trials: 10, minibatch_size: 2, ..Default::default() }); let result = mipro .tune("Answer the question concisely:", &dataset, &ExactMatchEvaluator, &llm) .unwrap(); println!("MIPROv2 best prompt: {}", result.prompt); println!("MIPROv2 best score: {:.2}", result.score); // GEPA: evolutionary population search let gepa = Gepa::default(); let gepa_result = gepa .tune("Provide accurate answers:", &dataset, &ExactMatchEvaluator, &llm) .unwrap(); println!("GEPA best prompt: {}", gepa_result.prompt); // Noop: pass-through for baseline comparison let noop = Noop; let baseline = noop .tune("Original prompt:", &dataset, &ExactMatchEvaluator, &llm) .unwrap(); println!("Baseline prompt: {}", baseline.prompt); } ``` -------------------------------- ### Initialize and Query Knowledge Graph in Rust Source: https://context7.com/afstanton/fornix/llms.txt Demonstrates connecting to a memory-backed graph, creating entities and causal relations, and performing traversals or community detection. ```rust use fornix::graph::{GraphAdapter, GraphConfig, adapters::MemoryGraphAdapter}; use fornix::graph::adapter::{EntitySearchOptions, RelationOptions, CausalOptions, TraversalDirection}; use std::collections::HashMap; #[tokio::main] async fn main() { let graph = MemoryGraphAdapter::connect(GraphConfig::default()).await.unwrap(); // Create entities with properties let mut props = HashMap::new(); props.insert("severity".to_string(), serde_json::json!("high")); let rain = graph.create_entity("Heavy Rain", "Weather", Some(props), None).await.unwrap(); let flood = graph.create_entity("Flooding", "Event", None, None).await.unwrap(); let damage = graph.create_entity("Property Damage", "Outcome", None, None).await.unwrap(); println!("Created entity: {} (ID: {})", rain.name, rain.id); // Create causal relations graph.create_relation(rain.id, flood.id, "CAUSES", None, None).await.unwrap(); graph.create_relation(flood.id, damage.id, "CAUSES", None, None).await.unwrap(); // Search entities by type and query let weather_events = graph .search_entities( EntitySearchOptions::new() .with_type("Weather") .with_limit(10), None, ) .await .unwrap(); println!("Found {} weather entities", weather_events.len()); // Traverse neighbors let neighbors = graph .neighbors(rain.id, 2, TraversalDirection::Outgoing, None) .await .unwrap(); println!("Neighbors within 2 hops: {:?}", neighbors.iter().map(|e| &e.name).collect::>()); // Find causal descendants (effects of rain) let effects = graph .causal_descendants( rain.id, CausalOptions { types: Some(vec!["CAUSES".to_string()]), max_depth: 5, max_paths: 50, ..Default::default() }, None, ) .await .unwrap(); for path in effects { println!("Causal chain: {:?}", path.entities.iter().map(|e| &e.name).collect::>()); } // Find shortest path between entities let path = graph.shortest_path(rain.id, damage.id, None).await.unwrap(); println!("Shortest path: {:?}", path.iter().map(|e| &e.name).collect::>()); // Detect communities let communities = graph.detect_communities(None).await.unwrap(); println!("Detected {} communities", communities.len()); } ``` -------------------------------- ### Connect and Search with MemoryVectorAdapter Source: https://github.com/afstanton/fornix/blob/main/README.md Demonstrates initializing a memory-based vector adapter and performing upsert and nearest neighbor search operations. ```rust use fornix::vector::{VectorAdapter, VectorConfig, adapters::MemoryVectorAdapter}; use fornix::vector::adapter::SearchOptions; #[tokio::main] async fn main() { let adapter = MemoryVectorAdapter::connect(VectorConfig::with_dimension(2)) .await .unwrap(); adapter.upsert("doc-1", vec![1.0, 0.0], None, None).await.unwrap(); let results = adapter .nearest_neighbors(&[1.0, 0.0], None, SearchOptions::default()) .await .unwrap(); println!("top id: {}", results[0].id); } ``` -------------------------------- ### LLM Router Strategies in Rust Source: https://context7.com/afstanton/fornix/llms.txt Demonstrates various routing strategies for LLM model selection, including regex, round-robin, weighted random, embedding threshold, and RoRF. Ensure necessary imports are present. ```rust use fornix::router::{ RegexStrategy, RegexRule, RoundRobin, WeightedRandom, WeightedModel, EmbeddingThreshold, EmbeddingThresholdConfig, RoRFStrategy, RoutingStrategy, ForestParams, }; fn main() { // Regex-based routing let regex_router = RegexStrategy::new(vec![ RegexRule { pattern: r"(?i)code|programming|debug".to_string(), model: "claude-opus-4".to_string(), provider: "anthropic".to_string(), }, RegexRule { pattern: r"(?i)translate|summarize".to_string(), model: "claude-sonnet-4".to_string(), provider: "anthropic".to_string(), }, ], "claude-haiku-4-5-20251001".to_string(), "anthropic".to_string()); let decision = regex_router.route("Help me debug this code", None, &[]).unwrap(); println!("Regex routing: {} ({})", decision.model, decision.provider); // Round-robin for load distribution let round_robin = RoundRobin::new(vec![ ("claude-sonnet-4".to_string(), "anthropic".to_string()), ("gpt-4o".to_string(), "openai".to_string()), ]); for i in 0..4 { let d = round_robin.route("Any query", None, &[]).unwrap(); println!("Round robin {}: {}", i, d.model); } // Weighted random selection let weighted = WeightedRandom::new(vec![ WeightedModel { model: "claude-opus-4".into(), provider: "anthropic".into(), weight: 0.2 }, WeightedModel { model: "claude-sonnet-4".into(), provider: "anthropic".into(), weight: 0.8 }, ]); let d = weighted.route("General query", None, &[]).unwrap(); println!("Weighted random: {}", d.model); // Embedding threshold for complexity routing let threshold_router = EmbeddingThreshold::new(EmbeddingThresholdConfig { strong_model: "claude-opus-4".to_string(), strong_provider: "anthropic".to_string(), weak_model: "claude-haiku-4-5-20251001".to_string(), weak_provider: "anthropic".to_string(), threshold: 0.7, centroid: vec![0.5, 0.5, 0.5], }); // High complexity query (far from centroid) -> strong model let d = threshold_router.route("Complex query", Some(&[0.1, 0.9, 0.1]), &[]).unwrap(); println!("Threshold routing: {}", d.model); // RoRF: Learned routing via Random Forest let features: Vec> = vec![ vec![0.1, 0.2], // Simple query embedding vec![0.9, 0.8], // Complex query embedding ]; let labels: Vec = vec![1, 0]; // 1 = weak model, 0 = strong model let rorf = RoRFStrategy::train( &features, &labels, 0.5, "claude-opus-4", "anthropic", "claude-haiku-4-5-20251001", "anthropic", ForestParams::default(), ).unwrap(); let d = rorf.route("Test query", Some(&[0.85, 0.75]), &[]).unwrap(); println!("RoRF routing: {} (confidence: {:.2})", d.model, d.confidence); } ``` -------------------------------- ### Vector Search Operations Source: https://context7.com/afstanton/fornix/llms.txt Demonstrates how to initialize a vector adapter, upsert vectors with metadata, perform nearest-neighbor searches with similarity thresholds, and manage vector storage. ```APIDOC ## Vector Search API ### Description The vector module provides nearest-neighbor search capabilities using the `VectorAdapter` trait. It supports upserting vectors with optional metadata, searching with similarity constraints, and basic storage management. ### Method Rust Trait Methods (Asynchronous) ### Parameters #### Upsert Parameters - **id** (string) - Required - Unique identifier for the document - **vector** (Vec) - Required - Embedding vector - **metadata** (Option>) - Optional - Key-value metadata associated with the vector - **namespace** (Option) - Optional - Namespace for partitioning #### Search Parameters - **query_vector** (Vec) - Required - The vector to search against - **namespace** (Option) - Optional - Namespace to filter search - **options** (SearchOptions) - Optional - Configuration including limit, min_similarity, and include_vectors flag ### Request Example // Upserting a vector adapter.upsert("doc-1", vec![0.1; 384], Some(metadata), None).await; // Searching for neighbors adapter.nearest_neighbors(&query_vector, None, SearchOptions::default().with_limit(10)).await; ### Response #### Success Response (200) - **results** (Vec) - A list of search results containing IDs and similarity scores. #### Response Example [ { "id": "doc-1", "score": 0.9821 }, { "id": "doc-2", "score": 0.7543 } ] ``` -------------------------------- ### Configure and Run Agent Engine Source: https://context7.com/afstanton/fornix/llms.txt Configure the agent's policy with restrictions and build the engine with the custom model client and tool registry. The `solve` method executes the agent's task. ```rust fn main() { // Configure policy with workspace restrictions let policy = Policy::new(PathBuf::from("/workspace")) .with_allowed_commands(vec!["ls", "cat", "grep"]) .with_blocked_paths(vec!["/etc", "/root"]); // Build engine let engine = Engine::new( MyModelClient, MyTools, policy, "You are a helpful assistant with access to search and calculator tools.", ); // Configure execution let config = CallConfig { model: "claude-sonnet-4".to_string(), provider: "anthropic".to_string(), max_steps_per_call: 10, max_depth: 3, max_total_tokens: Some(100_000), max_solve_seconds: 60.0, ..Default::default() }; // Solve with event callback let result = engine.solve( "Analyze the performance metrics and calculate the improvement percentage", &config, Some(&|event| println!("[Event] {}", event)), ); match result { Ok(answer) => println!("Answer:\n{}", answer), Err(e) => eprintln!("Error: {}", e), } } ``` -------------------------------- ### Run All Tests with Full Features Source: https://github.com/afstanton/fornix/blob/main/README.md Executes all tests within the project, including those requiring the 'full' feature set. ```bash cargo test --features full ``` -------------------------------- ### Implement ToolRegistry for Available Tools Source: https://context7.com/afstanton/fornix/llms.txt Implement the `ToolRegistry` trait to define and execute available tools. The `tool_definitions` method provides metadata for each tool, and `execute` handles tool invocation. ```rust // Implement ToolRegistry for available tools struct MyTools; impl ToolRegistry for MyTools { fn tool_definitions(&self) -> Vec { vec![ ToolDef { name: "search".to_string(), description: "Search the knowledge base".to_string(), parameters: serde_json::json!({ "type": "object", "properties": { "query": { "type": "string" } }, "required": ["query"] }), }, ToolDef { name: "calculator".to_string(), description: "Perform mathematical calculations".to_string(), parameters: serde_json::json!({ "type": "object", "properties": { "expression": { "type": "string" } }, "required": ["expression"] }), }, ] } fn execute(&self, call: &ToolCall) -> ToolResult { match call.name.as_str() { "search" => { let query = call.arg_str("query").unwrap_or("unknown"); ToolResult::ok(&call.id, &call.name, format!("Results for '{}': ...", query)) } "calculator" => { let expr = call.arg_str("expression").unwrap_or("0"); ToolResult::ok(&call.id, &call.name, format!("Result: {}", expr)) } _ => ToolResult::err(&call.id, &call.name, "Unknown tool"), } } } ``` -------------------------------- ### Define and Validate Domain Ontology in Rust Source: https://context7.com/afstanton/fornix/llms.txt Demonstrates building a regulatory domain ontology, defining entity and relation types, validating properties, and generating LLM prompts. ```rust use std::sync::Arc; use fornix::ontology::{ Definition, EntityTypeDefinition, RelationTypeDefinition, PropertyDefinition, OntologyValidator, OntologyPrompt, ValidationRules, }; use fornix::common::metadata::Metadata; fn main() { // Build a domain ontology let mut ontology = Definition::new("regulatory"); ontology.version = Some("1.0.0".to_string()); ontology.description = Some("US federal regulatory domain".to_string()); // Define entity types with properties and aliases ontology.entity_types.push(EntityTypeDefinition { name: "Regulation".to_string(), description: Some("A codified rule in the CFR.".to_string()), extraction_strategy: Some("llm".to_string()), extraction_patterns: Vec::new(), aliases: vec!["Provision".to_string(), "Rule".to_string()], properties: vec![ PropertyDefinition { name: "cfr_citation".to_string(), data_type: "string".to_string(), required: true, validation_rules: ValidationRules { pattern: Some(r"^\d+ CFR \d+".to_string()), allowed_values: Vec::new(), }, }, PropertyDefinition::optional("effective_date", "date"), ], }); ontology.entity_types.push(EntityTypeDefinition { name: "Agency".to_string(), description: Some("A federal regulatory agency.".to_string()), extraction_strategy: None, extraction_patterns: Vec::new(), aliases: vec!["Bureau".to_string()], properties: vec![PropertyDefinition::optional("acronym", "string")], }); // Define relation types with source/target constraints ontology.relation_types.push(RelationTypeDefinition { name: "ISSUED_BY".to_string(), description: Some("Links a regulation to its issuing agency.".to_string()), source_types: vec!["Regulation".to_string()], target_types: vec!["Agency".to_string()], properties: Vec::new(), }); let ont = Arc::new(ontology); // Validate entities during extraction let validator = OntologyValidator::new(&ont); // Check known types (including aliases) assert!(validator.known_entity_type("Regulation")); assert!(validator.known_entity_type("Provision")); // alias assert!(validator.known_entity_type("Bureau")); // alias for Agency // Resolve aliases to canonical names assert_eq!(validator.canonical_entity_type("Provision"), Some("Regulation")); assert_eq!(validator.canonical_entity_type("Bureau"), Some("Agency")); // Validate extracted entity properties let mut props: Metadata = Metadata::new(); props.insert("cfr_citation".to_string(), serde_json::json!("42 CFR 11.1")); let result = validator.validate_entity("Regulation", &props); assert!(result.is_valid()); // Invalid property pattern let mut bad_props: Metadata = Metadata::new(); bad_props.insert("cfr_citation".to_string(), serde_json::json!("invalid")); let bad_result = validator.validate_entity("Regulation", &bad_props); assert!(!bad_result.is_valid()); println!("Validation errors: {:?}", bad_result.error_messages()); // Validate relations let rel_result = validator.validate_relation( "ISSUED_BY", "Regulation", // source type "Agency", // target type &Metadata::new(), ); assert!(rel_result.is_valid()); // Build LLM extraction prompts from ontology let prompt = OntologyPrompt::build_entity_prompt(&ont, "Regulation").unwrap(); println!("LLM Prompt:\n{}", prompt); // JSON serialization for Ruby boundary let json = ont.to_json().unwrap(); let restored = Definition::from_json(&json).unwrap(); assert_eq!(restored.name, "regulatory"); } ``` -------------------------------- ### Implement BM25 Full-Text Search Source: https://context7.com/afstanton/fornix/llms.txt Demonstrates indexing single and multi-field documents and performing searches with specific options. ```rust use fornix::bm25::{Bm25Config, Bm25Adapter, adapters::MemoryBm25Adapter}; use fornix::bm25::adapter::{IndexDocument, SearchOptions}; #[tokio::main] async fn main() { let adapter = MemoryBm25Adapter::connect(Bm25Config::default()).await.unwrap(); // Index single-field documents adapter.index( IndexDocument::new("doc-1", "Rust is a systems programming language focused on safety"), None, ).await.unwrap(); adapter.index( IndexDocument::new("doc-2", "Python is great for scripting and data science"), None, ).await.unwrap(); // Index multi-field documents adapter.index( IndexDocument::with_fields("doc-3", [ ("title", "Machine Learning Guide"), ("body", "Neural networks and deep learning fundamentals"), ]), None, ).await.unwrap(); // Search with options let results = adapter .search( "rust systems", None, SearchOptions::default() .with_limit(10) .with_min_score(0.1), ) .await .unwrap(); for result in results { println!("ID: {}, BM25 Score: {:.4}", result.id, result.score); } // Search specific fields only let field_results = adapter .search( "neural networks", None, SearchOptions::default().with_fields(["body"]), ) .await .unwrap(); println!("Field search found {} results", field_results.len()); } ``` -------------------------------- ### Implement ModelClient for LLM Provider Source: https://context7.com/afstanton/fornix/llms.txt Implement the `ModelClient` trait to integrate your LLM provider. The `complete` function handles message processing and returns a `ModelTurn`. ```rust use fornix::agent::{Engine, Policy, CallConfig, Message, ModelTurn, ToolCall, ToolDef, ToolResult}; use fornix::agent::traits::{ModelClient, ToolRegistry}; use std::path::PathBuf; // Implement ModelClient for your LLM provider struct MyModelClient; impl ModelClient for MyModelClient { fn complete( &self, messages: &[Message], _tools: &[ToolDef], config: &CallConfig, system: &str, ) -> fornix::agent::Result { // In production: call OpenAI, Anthropic, etc. let last_content = messages.last().map(|m| m.content.as_str()).unwrap_or(""); Ok(ModelTurn { text: format!("Based on '{}', here's my analysis...", &last_content[..last_content.len().min(50)]), tool_calls: vec![], // No tools needed for this response input_tokens: 100, output_tokens: 50, }) } } ``` -------------------------------- ### Define Ontology and Configure GraphRAG in Rust Source: https://context7.com/afstanton/fornix/llms.txt Demonstrates how to define entity and relation types using the Fornix ontology structures and initialize a GraphRagConfig instance. ```rust use std::sync::Arc; use fornix::ontology::{Definition, EntityTypeDefinition, RelationTypeDefinition}; use fornix::graphrag::{GraphRagConfig, LocalSearch, GlobalSearch, HybridSearch as GraphRagHybrid}; fn main() { // Build ontology for GraphRAG let mut def = Definition::new("knowledge"); def.version = Some("1.0.0".to_string()); def.entity_types.push(EntityTypeDefinition { name: "Person".to_string(), description: Some("A human individual.".to_string()), extraction_strategy: Some("llm".to_string()), extraction_patterns: Vec::new(), aliases: Vec::new(), properties: Vec::new(), }); def.entity_types.push(EntityTypeDefinition { name: "Organization".to_string(), description: Some("A company or institution.".to_string()), extraction_strategy: Some("llm".to_string()), extraction_patterns: Vec::new(), aliases: vec!["Company".to_string()], properties: Vec::new(), }); def.relation_types.push(RelationTypeDefinition { name: "WORKS_AT".to_string(), description: Some("Employment relationship.".to_string()), source_types: vec!["Person".to_string()], target_types: vec!["Organization".to_string()], properties: Vec::new(), }); let ontology = Arc::new(def); // Configure GraphRAG with ontology let config = GraphRagConfig { ontology: Some(Arc::clone(&ontology)), local_search_depth: 2, community_level: 1, ..Default::default() }; // Entity types are derived from ontology let entity_types = config.effective_entity_types(); assert!(entity_types.contains(&"Person".to_string())); assert!(entity_types.contains(&"Organization".to_string())); let relation_types = config.effective_relation_types(); assert!(relation_types.contains(&"WORKS_AT".to_string())); println!("Configured entity types: {:?}", entity_types); println!("Configured relation types: {:?}", relation_types); } ``` -------------------------------- ### Run Ontology Module Tests Source: https://github.com/afstanton/fornix/blob/main/README.md Executes tests specifically for the 'ontology' module, targeting the 'fornix' crate with the 'ontology' feature enabled. ```bash cargo test --features ontology -p fornix ``` -------------------------------- ### Serialize and Deserialize Ontology Definitions Source: https://github.com/afstanton/fornix/blob/main/README.md Demonstrates JSON serialization and deserialization for Definition objects, useful for cross-language boundaries. ```rust use fornix::ontology::Definition; let json = my_definition.to_json().unwrap(); // Pass JSON string to Ruby via Magnus; deserialise on the way back: let def = Definition::from_json(&json).unwrap(); ``` -------------------------------- ### Configure Fornix Dependencies Source: https://context7.com/afstanton/fornix/llms.txt Define dependencies in Cargo.toml using feature flags to include only necessary modules. ```toml # Selective features [dependencies] fornix = { version = "0.4", features = ["vector", "bm25", "hybrid"] } # Or enable everything [dependencies] fornix = { version = "0.4", features = ["full"] } ``` -------------------------------- ### Define and Validate Ontologies for GraphRAG Source: https://github.com/afstanton/fornix/blob/main/README.md Shows how to construct an ontology with entity and relation types, validate types, and configure GraphRAG to enforce these constraints. ```rust use std::sync::Arc; use fornix::ontology::{Definition, EntityTypeDefinition, RelationTypeDefinition, PropertyDefinition, OntologyValidator, OntologyPrompt}; use fornix::graphrag::GraphRagConfig; fn main() { // Build an ontology let mut def = Definition::new("regulatory"); def.version = Some("1.0.0".to_string()); def.entity_types.push(EntityTypeDefinition { name: "Regulation".to_string(), description: Some("A codified rule in the CFR.".to_string()), extraction_strategy: Some("llm".to_string()), extraction_patterns: Vec::new(), aliases: vec!["Provision".to_string()], properties: vec![PropertyDefinition::required("cfr_citation", "string")], }); def.relation_types.push(RelationTypeDefinition { name: "ISSUED_BY".to_string(), description: None, source_types: vec!["Regulation".to_string()], target_types: vec!["Agency".to_string()], properties: Vec::new(), }); let ont = Arc::new(def); // Validate during extraction let validator = OntologyValidator::new(&ont); assert!(validator.known_entity_type("Regulation")); assert_eq!(validator.canonical_entity_type("Provision"), Some("Regulation")); // Build per-type prompt guidance let prompt = OntologyPrompt::build_entity_prompt(&ont, "Regulation").unwrap(); println!("{}", prompt); // Configure GraphRAG to use the ontology let config = GraphRagConfig { ontology: Some(Arc::clone(&ont)), ..Default::default() }; // effective_entity_types() now derives from the ontology let types = config.effective_entity_types(); assert!(types.contains(&"Regulation".to_string())); assert!(!types.contains(&"Person".to_string())); // default fallback not used } ``` -------------------------------- ### Run Clippy Across All Targets and Features Source: https://github.com/afstanton/fornix/blob/main/README.md Performs linting using Clippy across all available targets and feature flags to ensure code quality. ```bash cargo clippy --all-targets --all-features ``` -------------------------------- ### Reference Fornix Feature Flags Source: https://context7.com/afstanton/fornix/llms.txt List of available feature flags and their module dependencies. ```toml # Available features and their dependencies: # store - base adapter traits, config, health # cache - caching adapters (depends: store) # vector - vector storage and nearest-neighbor search (depends: store) # bm25 - BM25 keyword retrieval (depends: store) # hybrid - fused vector + BM25 retrieval (depends: vector, bm25) # ontology - domain-aware type schemas (depends: store) # graph - knowledge graph with bitemporal APIs (depends: store) # rag - chunking, filters, eval, reranking (depends: hybrid) # graphrag - graph-augmented retrieval (depends: graph, rag, hybrid, ontology) # router - model routing strategies (depends: cache) # diff - text diffing and snippets # tuner - prompt optimization (depends: rag) # agent - autonomous tool-using runtime (depends: router) # full - enables all modules ``` -------------------------------- ### Implement Hybrid Search Source: https://context7.com/afstanton/fornix/llms.txt Shows how to combine BM25 and vector search using RRF or weighted linear combination. ```rust use fornix::bm25::{adapters::MemoryBm25Adapter, Bm25Config, Bm25Adapter}; use fornix::bm25::adapter::IndexDocument; use fornix::vector::{adapters::MemoryVectorAdapter, VectorAdapter, VectorConfig}; use fornix::hybrid::{HybridConfig, HybridSearch, search::HybridSearchOptions}; use fornix::hybrid::config::{FusionMethod, NormalisationMethod}; #[tokio::main] async fn main() { // Initialize both adapters let bm25 = MemoryBm25Adapter::connect(Bm25Config::default()).await.unwrap(); let vector = MemoryVectorAdapter::connect(VectorConfig::with_dimension(3)).await.unwrap(); // Index documents in both stores bm25.index(IndexDocument::new("rust", "rust systems programming language"), None).await.unwrap(); bm25.index(IndexDocument::new("python", "python scripting easy language"), None).await.unwrap(); bm25.index(IndexDocument::new("go", "go concurrency goroutines channels"), None).await.unwrap(); vector.upsert("rust", vec![1.0, 0.0, 0.0], None, None).await.unwrap(); vector.upsert("python", vec![0.7, 0.7, 0.0], None, None).await.unwrap(); vector.upsert("go", vec![0.0, 1.0, 0.0], None, None).await.unwrap(); // Configure hybrid search with custom weights let config = HybridConfig { fusion: FusionMethod::Rrf, bm25_weight: 0.4, vector_weight: 0.6, rrf_k: 60, normalisation: NormalisationMethod::MinMax, bm25_candidates: 100, vector_candidates: 100, min_similarity: Some(0.3), }; let search = HybridSearch::new(bm25, vector, config); // Execute hybrid search let results = search .search( "rust programming", // text query for BM25 &[0.9, 0.1, 0.0], // embedding for vector search None, // namespace HybridSearchOptions::new() .with_limit(5), ) .await .unwrap(); for result in results { println!( "ID: {}, Hybrid: {:.4}, BM25: {:?}, Vector: {:?}, Confidence: {:?}", result.id, result.hybrid_score, result.bm25_score, result.vector_score, result.confidence_score, ); } } ``` -------------------------------- ### Graph Causal Descendants Traversal in Rust Source: https://github.com/afstanton/fornix/blob/main/README.md Demonstrates creating entities and relations, then performing a causal descendants traversal on a graph using the Fornix library. Requires `tokio` for async operations. ```rust use fornix::graph::{adapters::MemoryGraphAdapter, GraphAdapter, GraphConfig}; use fornix::graph::adapter::CausalOptions; #[tokio::main] async fn main() { let graph = MemoryGraphAdapter::connect(GraphConfig::default()).await.unwrap(); let rain = graph.create_entity("Heavy Rain", "Weather", None, None).await.unwrap(); let flood = graph.create_entity("Flooding", "Event", None, None).await.unwrap(); graph.create_relation(rain.id, flood.id, "CAUSES", None, None) .await .unwrap(); let paths = graph .causal_descendants(rain.id, CausalOptions::default(), None) .await .unwrap(); println!("first relation: {}", paths[0].edges[0].relation_type); } ``` -------------------------------- ### Implement TTL-aware caching with Fornix Source: https://context7.com/afstanton/fornix/llms.txt Configures a memory cache adapter and manages entries using deterministic keys, custom TTLs, and cache statistics. ```rust use fornix::cache::{CacheConfig, CacheAdapter, CacheKey, adapters::MemoryCacheAdapter}; use std::time::Duration; #[tokio::main] async fn main() { let config = CacheConfig { default_ttl: Some(Duration::from_secs(3600)), // 1 hour default max_entries: Some(10000), ..Default::default() }; let cache = MemoryCacheAdapter::connect(config).await.unwrap(); // Build deterministic cache keys let key = CacheKey::new() .with_prefix("embedding") .with_model("text-embedding-3-small") .with_input("Hello, world!") .build(); println!("Cache key: {}", key); // Store with custom TTL cache.set( &key, b"[0.1, 0.2, 0.3, ...]".to_vec(), Some(Duration::from_secs(7200)), // 2 hours None, // namespace ).await.unwrap(); // Retrieve if let Some(value) = cache.get(&key, None).await.unwrap() { println!("Cache hit: {} bytes", value.len()); } // Cache stats let stats = cache.stats().await.unwrap(); println!("Hits: {}, Misses: {}, Hit rate: {:.2}%", stats.hits, stats.misses, stats.hit_rate() * 100.0); // Delete specific key cache.delete(&key, None).await.unwrap(); // Clear entire cache cache.clear(None).await.unwrap(); } ``` -------------------------------- ### Implement Vector Search Source: https://context7.com/afstanton/fornix/llms.txt Perform nearest-neighbor search using the MemoryVectorAdapter with upsert, search, and deletion operations. ```rust use fornix::vector::{VectorAdapter, VectorConfig, adapters::MemoryVectorAdapter}; use fornix::vector::adapter::SearchOptions; #[tokio::main] async fn main() { // Create adapter with 384-dimensional vectors let adapter = MemoryVectorAdapter::connect(VectorConfig::with_dimension(384)) .await .unwrap(); // Upsert vectors with optional metadata adapter.upsert( "doc-1", vec![0.1; 384], // embedding vector Some([("category".to_string(), serde_json::json!("science"))].into_iter().collect()), None, // namespace ).await.unwrap(); adapter.upsert("doc-2", vec![0.2; 384], None, None).await.unwrap(); adapter.upsert("doc-3", vec![0.9; 384], None, None).await.unwrap(); // Search with options let query_vector = vec![0.15; 384]; let results = adapter .nearest_neighbors( &query_vector, None, SearchOptions::default() .with_limit(10) .with_min_similarity(0.5) .include_vectors(), ) .await .unwrap(); for result in results { println!("ID: {}, Score: {:.4}", result.id, result.score()); } // Count and delete operations let count = adapter.count(None).await.unwrap(); println!("Total vectors: {}", count); adapter.delete("doc-3", None).await.unwrap(); } ``` -------------------------------- ### Build and Validate Ontology with Fornix Source: https://context7.com/afstanton/fornix/llms.txt Define entity and relation types, then configure the graph to validate writes against this ontology. Strict mode raises errors on violations. ```rust use std::sync::Arc; use fornix::ontology::{Definition, EntityTypeDefinition, RelationTypeDefinition}; use fornix::graph::{GraphConfig, GraphAdapter, adapters::MemoryGraphAdapter}; #[tokio::main] async fn main() { // Build ontology let mut def = Definition::new("corporate"); def.version = Some("1.0.0".to_string()); def.entity_types.push(EntityTypeDefinition { name: "Employee".to_string(), description: Some("A company employee.".to_string()), extraction_strategy: None, extraction_patterns: Vec::new(), aliases: vec!["Worker".to_string(), "Staff".to_string()], properties: Vec::new(), }); def.entity_types.push(EntityTypeDefinition { name: "Department".to_string(), description: Some("An organizational unit.".to_string()), extraction_strategy: None, extraction_patterns: Vec::new(), aliases: Vec::new(), properties: Vec::new(), }); def.relation_types.push(RelationTypeDefinition { name: "BELONGS_TO".to_string(), description: None, source_types: vec!["Employee".to_string()], target_types: vec!["Department".to_string()], properties: Vec::new(), }); let ontology = Arc::new(def); // Configure graph with ontology validation let config = GraphConfig { ontology: Some(Arc::clone(&ontology)), ontology_strict: true, // Violations raise errors ..Default::default() }; // Validate entity types before creation match config.validate_entity_type("Worker") { Ok(canonical) => println!("Canonical type: {}", canonical), // "Employee" Err(e) => eprintln!("Validation error: {}", e), } // Invalid type in strict mode match config.validate_entity_type("InvalidType") { Ok(_) => println!("Unexpectedly valid"), Err(e) => println!("Expected error: {}", e), // OntologyViolation } // Create graph adapter with validated config let graph = MemoryGraphAdapter::connect(config).await.unwrap(); // Entity creation uses validated types let emp = graph.create_entity("John Doe", "Employee", None, None).await.unwrap(); let dept = graph.create_entity("Engineering", "Department", None, None).await.unwrap(); // Relation respects source/target constraints graph.create_relation(emp.id, dept.id, "BELONGS_TO", None, None).await.unwrap(); println!("Created employee {} in department {}", emp.name, dept.name); } ``` -------------------------------- ### Output Filter Pipeline for RAG Results Source: https://context7.com/afstanton/fornix/llms.txt Chains multiple output filters to refine retrieval results based on score, deduplication, and quantity. Requires `fornix::rag::FilterPipeline` and various filter types. ```rust use fornix::rag::{FilterPipeline, MinScoreFilter, DeduplicateFilter, TruncateFilter}; use fornix::rag::types::FilteredResult; let pipeline = FilterPipeline::new(vec![ Box::new(MinScoreFilter::new(0.5)), // Remove low scores Box::new(DeduplicateFilter::new(0.95)), // Remove near-duplicates Box::new(TruncateFilter::new(10)), // Keep top 10 ]); let results: Vec = vec![ FilteredResult { id: "a".into(), score: 0.9, text: "High quality result".into() }, FilteredResult { id: "b".into(), score: 0.3, text: "Low quality result".into() }, FilteredResult { id: "c".into(), score: 0.8, text: "Good result".into() }, ]; let filtered = pipeline.filter(results); println!("After filtering: {} results", filtered.len()); ``` -------------------------------- ### Perform Hybrid Retrieval with BM25 and Vector Search Source: https://github.com/afstanton/fornix/blob/main/README.md Combines BM25 keyword search and vector similarity search into a single hybrid retrieval workflow. ```rust use fornix::bm25::{adapters::MemoryBm25Adapter, adapter::IndexDocument, Bm25Adapter, Bm25Config}; use fornix::vector::{adapters::MemoryVectorAdapter, VectorAdapter, VectorConfig}; use fornix::hybrid::{HybridConfig, HybridSearch, search::HybridSearchOptions}; #[tokio::main] async fn main() { let bm25 = MemoryBm25Adapter::connect(Bm25Config::default()).await.unwrap(); let vector = MemoryVectorAdapter::connect(VectorConfig::with_dimension(2)).await.unwrap(); bm25.index(IndexDocument::new("doc-1", "rust systems programming"), None) .await .unwrap(); vector.upsert("doc-1", vec![1.0, 0.0], None, None).await.unwrap(); let search = HybridSearch::new(bm25, vector, HybridConfig::default()); let results = search .search("rust", &[1.0, 0.0], None, HybridSearchOptions::new()) .await .unwrap(); println!("hybrid top: {}", results[0].id); } ``` -------------------------------- ### Perform text diffing with Fornix Source: https://context7.com/afstanton/fornix/llms.txt Uses focused_pair for change markers and boundary_aware_stitched_pair for handling larger documents with specific unit boundaries. ```rust use fornix::diff::{focused_pair, boundary_aware_stitched_pair, Unit}; fn main() { let previous = "The quick brown fox jumps over the lazy dog. It was a sunny day."; let current = "The quick red fox leaps over the lazy cat. It was a rainy day."; // Basic focused diff with markers let (prev_snippet, curr_snippet) = focused_pair( previous, current, 1000, // max length 5, // context words around changes ); println!("Previous: {}", prev_snippet); // Output: The quick [[brown]] fox [[jumps]] over the lazy [[dog]]. It was a [[sunny]] day. println!("Current: {}", curr_snippet); // Output: The quick [[red]] fox [[leaps]] over the lazy [[cat]]. It was a [[rainy]] day. // Boundary-aware stitching for larger documents let long_prev = "Section 1: Introduction. The system processes data efficiently. \ Section 2: Methods. We use advanced algorithms. \ Section 3: Results. Performance improved by 50%."; let long_curr = "Section 1: Introduction. The system processes data very efficiently. \ Section 2: Methods. We use machine learning algorithms. \ Section 3: Results. Performance improved by 75%."; let stitched = boundary_aware_stitched_pair( long_prev, long_curr, 500, // max total length Unit::Sentence, // stitch at sentence boundaries 3, // context sentences ); println!("\nStitched previous:\n{}", stitched.previous); println!("\nStitched current:\n{}", stitched.current); } ``` -------------------------------- ### Query Gap Tracking for Corpus Improvement Source: https://context7.com/afstanton/fornix/llms.txt Tracks missed and hit queries to identify areas where the corpus may be lacking relevant information, aiding in corpus improvement. Uses `fornix::rag::QueryGapTracker`. ```rust use fornix::rag::QueryGapTracker; let mut tracker = QueryGapTracker::new(); tracker.record_miss("quantum entanglement explanation", 0.2); tracker.record_miss("quantum computing basics", 0.3); tracker.record_hit("rust programming tutorial", 0.9); let gaps = tracker.top_gaps(5); println!("Top corpus gaps: {:?}", gaps); ``` -------------------------------- ### Enforce Strict Ontology Validation for Graph Writes Source: https://github.com/afstanton/fornix/blob/main/README.md Configures a graph with strict ontology validation to ensure entity types conform to defined schemas. ```rust use std::sync::Arc; use fornix::ontology::Definition; use fornix::graph::GraphConfig; fn main() { let mut def = Definition::new("regulatory"); def.version = Some("1.0.0".to_string()); // ... populate entity/relation types ... let config = GraphConfig { ontology: Some(Arc::new(def)), ontology_strict: true, // violations raise OntologyViolation ..Default::default() }; // Call config.validate_entity_type("SomeType") before create_entity // to get the canonical name or an OntologyViolation error. match config.validate_entity_type("Provision") { Ok(canonical) => println!("canonical: {}", canonical), // "Regulation" Err(e) => eprintln!("violation: {}", e), } } ``` -------------------------------- ### Sentence-Aware Chunking Source: https://context7.com/afstanton/fornix/llms.txt Chunks documents based on sentences, allowing for a specified number of sentences per chunk and an overlap. Requires `fornix::rag::chunkers::SentenceToken`. ```rust use fornix::rag::chunkers::SentenceToken; let document = "..."; let sentence_chunker = SentenceToken::new(2, 1); // 2 sentences, 1 overlap let sentence_chunks = sentence_chunker.chunk(document); println!("Sentence chunks: {}", sentence_chunks.len()); for (i, chunk) in sentence_chunks.iter().enumerate() { println!("Chunk {}: {}", i, chunk.text); } ``` -------------------------------- ### Parent-Child Chunking for Hierarchical Retrieval Source: https://context7.com/afstanton/fornix/llms.txt Implements a hierarchical chunking strategy where parent chunks contain child chunks, useful for detailed retrieval. Uses `fornix::rag::chunkers::ParentChild`. ```rust use fornix::rag::chunkers::ParentChild; let document = "..."; let parent_child = ParentChild::new(200, 50, 10); // parent: 200, child: 50, overlap: 10 let hierarchical = parent_child.chunk(document); println!("Parent-child chunks: {}", hierarchical.len()); ```