Skip to main content

shodh_memory/query_parsing/
mod.rs

1//! Modular Query Parsing System
2//!
3//! Provides a trait-based abstraction for query parsing, allowing easy swapping
4//! between rule-based and LLM-based implementations.
5//!
6//! # Architecture
7//! ```text
8//! Query → QueryParser (trait) → ParsedQuery
9//!              ↓
10//!     ┌────────┴────────┐
11//!     │                 │
12//! RuleBasedParser   LlmParser
13//! (YAKE/regex)      (Qwen 1.5B)
14//! ```
15//!
16//! # Usage
17//! ```rust,ignore
18//! let parser = create_parser(ParserConfig::default());
19//! let parsed = parser.parse("When did Melanie paint a sunrise?", Some(conv_date))?;
20//! ```
21
22mod llm_parser;
23mod parser_trait;
24mod rule_based;
25
26pub use llm_parser::{ApiType, LlmParser};
27pub use parser_trait::*;
28pub use rule_based::RuleBasedParser;
29
30use std::sync::Arc;
31
32/// Parser implementation type
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
34pub enum ParserType {
35    /// Rule-based parsing using YAKE, regex, and heuristics (default)
36    #[default]
37    RuleBased,
38    /// LLM-based parsing using Qwen 1.5B or similar
39    Llm,
40}
41
42/// Configuration for the query parser
43#[derive(Debug, Clone)]
44pub struct ParserConfig {
45    /// Which parser implementation to use
46    pub parser_type: ParserType,
47    /// Path to LLM model (only used if parser_type is Llm)
48    pub llm_model_path: Option<String>,
49    /// Number of threads for LLM inference
50    pub llm_threads: usize,
51    /// Context size for LLM
52    pub llm_context_size: usize,
53}
54
55impl Default for ParserConfig {
56    fn default() -> Self {
57        Self {
58            parser_type: ParserType::RuleBased,
59            llm_model_path: None,
60            llm_threads: 4,
61            llm_context_size: 2048,
62        }
63    }
64}
65
66impl ParserConfig {
67    /// Create config for rule-based parser
68    pub fn rule_based() -> Self {
69        Self::default()
70    }
71
72    /// Create config for LLM parser
73    pub fn llm(model_path: impl Into<String>) -> Self {
74        Self {
75            parser_type: ParserType::Llm,
76            llm_model_path: Some(model_path.into()),
77            ..Default::default()
78        }
79    }
80}
81
82/// Create a parser based on configuration
83pub fn create_parser(config: ParserConfig) -> Arc<dyn QueryParser> {
84    match config.parser_type {
85        ParserType::RuleBased => Arc::new(RuleBasedParser::new()),
86        #[cfg(feature = "llm-parser")]
87        ParserType::Llm => {
88            let model_path = config
89                .llm_model_path
90                .expect("LLM model path required for LLM parser");
91            Arc::new(
92                LlmParser::new(&model_path, config.llm_threads, config.llm_context_size)
93                    .expect("Failed to load LLM model"),
94            )
95        }
96        #[cfg(not(feature = "llm-parser"))]
97        ParserType::Llm => {
98            tracing::warn!("LLM parser requested but 'llm-parser' feature not enabled, falling back to rule-based");
99            Arc::new(RuleBasedParser::new())
100        }
101    }
102}