Skip to main content

cordance_llm/
lib.rs

1//! Cordance LLM adapters. Bounded candidate prose only. ADR 0002.
2//!
3//! LLM output is **never** authority. Every claim must cite a source-ID from the
4//! input pack. Hard rules cannot originate here.
5//!
6//! Enforced at the adapter level:
7//! - **Local-only by default.** Non-loopback Ollama URLs are rejected unless
8//!   `CORDANCE_ALLOW_REMOTE_LLM=1` is set in the operator's environment.
9//! - **Schema-bounded responses.** Outputs deserialise into a fixed
10//!   `LlmCandidate` shape; arbitrary text never escapes the adapter.
11//! - **4-gram source grounding.** Every claim's text must share at least one
12//!   4-gram with a cited source body, else the claim is rejected before the
13//!   candidate leaves the adapter.
14//! - **Hard rules dropped.** `ClaimType::HardRule` and `ClaimType::ProjectInvariant`
15//!   are filtered regardless of grounding — those classes can only come from
16//!   doctrine, ADRs, or schemas.
17//!
18//! # Golden path
19//!
20//! ```no_run
21//! use std::collections::HashMap;
22//! use cordance_llm::{OllamaAdapter, OllamaSettings};
23//!
24//! struct Cfg;
25//! impl OllamaSettings for Cfg {
26//!     fn base_url(&self) -> &str { "http://localhost:11434" }
27//!     fn model(&self) -> &str { "qwen2.5-coder:14b" }
28//!     fn temperature(&self) -> f32 { 0.1 }
29//!     fn num_ctx(&self) -> u32 { 8192 }
30//! }
31//!
32//! let adapter = OllamaAdapter::from_config(&Cfg);
33//! if !adapter.is_available() {
34//!     eprintln!("Ollama not reachable at {}", adapter.base_url);
35//!     return;
36//! }
37//!
38//! let prompt = "Summarise this project's main purpose in one paragraph.";
39//! let cited_ids = vec!["project_readme:README.md".to_string()];
40//! let source_bodies: HashMap<String, String> = HashMap::new();
41//!
42//! let candidate = adapter
43//!     .generate_with_grounding(prompt, &cited_ids, &source_bodies)
44//!     .expect("ollama generate (grounded)");
45//! println!("got {} grounded claims", candidate.claims.len());
46//! ```
47
48#![forbid(unsafe_code)]
49#![deny(clippy::unwrap_used, clippy::expect_used)]
50#![cfg_attr(test, allow(clippy::expect_used, clippy::unwrap_used))]
51
52pub mod candidate;
53pub mod ollama;
54pub mod prompt;
55pub mod validator;
56
57pub use candidate::{ClaimType, LlmCandidate, LlmClaim};
58pub use ollama::{LlmError, OllamaAdapter, OllamaSettings};
59pub use validator::{validate, validate_with_sources};