kapsl-llm 0.1.0

Large language model inference with GGUF and ONNX backend support for Kapsl
Documentation
#[cfg(test)]
mod tests {
    use super::super::{
        build_rag_prompt, CitationStyle, RagChunk, RagPromptConfig, WhitespaceTokenCounter,
    };
    use std::collections::HashMap;

    fn chunk(id: &str, text: &str, score: f32, source: Option<&str>) -> RagChunk {
        let mut metadata = HashMap::new();
        if let Some(src) = source {
            metadata.insert("source".to_string(), src.to_string());
        }
        RagChunk {
            id: id.to_string(),
            text: text.to_string(),
            score,
            metadata,
        }
    }

    #[test]
    fn empty_chunks_produces_fallback() {
        let config = RagPromptConfig::default();
        let prompt = build_rag_prompt(&[], &config, &WhitespaceTokenCounter);
        assert!(prompt.context.is_empty());
        assert!(prompt.used_chunks.is_empty());
        assert!(prompt.fallback_message.is_some());
    }

    #[test]
    fn dedupe_and_per_source_limits_are_applied() {
        let chunks = vec![
            chunk("a", "Same text", 0.9, Some("doc1")),
            chunk("b", "Same text", 0.8, Some("doc1")),
            chunk("c", "Other text", 0.7, Some("doc2")),
        ];
        let config = RagPromptConfig {
            max_per_source: 1,
            max_chunks: 5,
            dedupe: true,
            ..Default::default()
        };

        let prompt = build_rag_prompt(&chunks, &config, &WhitespaceTokenCounter);
        assert_eq!(prompt.used_chunks.len(), 2);
    }

    #[test]
    fn truncation_and_inline_citations_work() {
        let chunks = vec![chunk("a", "one two three four", 0.5, Some("doc"))];
        let config = RagPromptConfig {
            max_context_tokens: 3,
            max_chunks: 1,
            truncate: true,
            citation_style: CitationStyle::Inline,
            ..Default::default()
        };

        let prompt = build_rag_prompt(&chunks, &config, &WhitespaceTokenCounter);
        assert!(prompt.truncated);
        assert_eq!(prompt.total_context_tokens, 3);
        assert!(prompt.context.contains("(1)"));
    }
}