diff --git a/Cargo.lock b/Cargo.lock index 281f4defd..a9ac892cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,17 +19,6 @@ dependencies = [ "cpufeatures 0.2.17", ] -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.17", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.12" @@ -694,7 +683,7 @@ version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "801927ee168e17809ab8901d9f01f700cd7d8d6a6527997fee44e4b0327a253c" dependencies = [ - "ahash 0.8.12", + "ahash", "async-trait", "cached_proc_macro", "cached_proc_macro_types", @@ -2787,9 +2776,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] [[package]] name = "hashbrown" @@ -2797,7 +2783,7 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.12", + "ahash", ] [[package]] @@ -3884,15 +3870,6 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "lru" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" -dependencies = [ - "hashbrown 0.12.3", -] - [[package]] name = "lru" version = "0.16.3" @@ -4075,29 +4052,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memoize" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8d1d5792299bab3f8b5d88d1b7a7cb50ad7ef039a8c4d45a6b84880a6526276" -dependencies = [ - "lazy_static", - "lru 0.7.8", - "memoize-inner", -] - -[[package]] -name = "memoize-inner" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd8f89255d8ff313afabed9a3c83ef0993cc056679dfd001f5111a026f876f7" -dependencies = [ - "lazy_static", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "mime" version = "0.3.17" @@ -5726,7 +5680,7 @@ dependencies = [ "indoc", "itertools 0.14.0", "kasuari", - "lru 0.16.3", + "lru", "strum", "thiserror 2.0.18", "unicode-segmentation", @@ -8169,7 +8123,7 @@ dependencies = [ name = "terraphim_agent" version = "1.15.0" dependencies = [ - "ahash 0.8.12", + "ahash", "anyhow", "async-trait", "chrono", @@ -8240,7 +8194,7 @@ dependencies = [ name = "terraphim_agent_messaging" version = "1.0.0" dependencies = [ - "ahash 0.8.12", + "ahash", "async-trait", "chrono", "env_logger", @@ -8263,7 +8217,7 @@ dependencies = [ name = "terraphim_agent_registry" version = "1.0.0" dependencies = [ - "ahash 0.8.12", + "ahash", "async-trait", "chrono", "criterion", @@ -8290,7 +8244,7 @@ dependencies = [ name = "terraphim_agent_supervisor" version = "1.0.0" dependencies = [ - "ahash 0.8.12", + "ahash", "async-trait", "chrono", "env_logger", @@ -8312,7 +8266,7 @@ dependencies = [ name = "terraphim_ai_nodejs" version = "1.0.0" dependencies = [ - "ahash 0.8.12", + "ahash", "anyhow", "napi", "napi-build", @@ -8361,7 +8315,7 @@ dependencies = [ name = "terraphim_automata" version = "1.4.10" dependencies = [ - "ahash 0.8.12", + "ahash", "aho-corasick", "anyhow", "bincode", @@ -8391,7 +8345,7 @@ dependencies = [ name = "terraphim_config" version = "1.4.10" dependencies = [ - "ahash 0.8.12", + "ahash", "anyhow", "async-once-cell", "async-trait", @@ -8426,7 +8380,7 @@ dependencies = [ name = "terraphim_goal_alignment" version = "1.0.0" dependencies = [ - "ahash 0.8.12", + "ahash", "async-trait", "chrono", "criterion", @@ -8507,7 +8461,7 @@ dependencies = [ name = "terraphim_kg_orchestration" version = "1.0.0" dependencies = [ - "ahash 0.8.12", + "ahash", "async-trait", "chrono", "env_logger", @@ -8533,7 +8487,7 @@ dependencies = [ name = "terraphim_mcp_server" version = "1.0.0" dependencies = [ - "ahash 0.8.12", + "ahash", "anyhow", "axum 0.8.8", "base64 0.21.7", @@ -8564,7 +8518,7 @@ dependencies = [ name = "terraphim_middleware" version = "1.15.0" dependencies = [ - "ahash 0.8.12", + "ahash", "async-trait", "cached", "dotenvy", @@ -8607,7 +8561,7 @@ dependencies = [ name = "terraphim_multi_agent" version = "1.0.0" dependencies = [ - "ahash 0.8.12", + "ahash", "anyhow", "async-trait", "chrono", @@ -8709,14 +8663,13 @@ dependencies = [ name = "terraphim_rolegraph" version = "1.4.10" dependencies = [ - "ahash 0.8.12", + "ahash", "aho-corasick", "anyhow", "criterion", "csv", "itertools 0.14.0", "log", - "memoize", "proptest", "regex", "serde", @@ -8764,7 +8717,7 @@ dependencies = [ name = "terraphim_server" version = "1.15.0" dependencies = [ - "ahash 0.8.12", + "ahash", "anyhow", "axum 0.8.8", "axum-test", @@ -8806,7 +8759,7 @@ dependencies = [ name = "terraphim_service" version = "1.4.10" dependencies = [ - "ahash 0.8.12", + "ahash", "anyhow", "async-trait", "chrono", @@ -8925,7 +8878,7 @@ dependencies = [ name = "terraphim_task_decomposition" version = "1.0.0" dependencies = [ - "ahash 0.8.12", + "ahash", "async-trait", "chrono", "criterion", @@ -9009,7 +8962,7 @@ dependencies = [ name = "terraphim_types" version = "1.6.0" dependencies = [ - "ahash 0.8.12", + "ahash", "anyhow", "chrono", "getrandom 0.3.4", @@ -9056,7 +9009,7 @@ dependencies = [ name = "terraphim_validation" version = "0.1.0" dependencies = [ - "ahash 0.8.12", + "ahash", "anyhow", "assert_cmd", "async-trait", diff --git a/crates/terraphim_automata/src/lib.rs b/crates/terraphim_automata/src/lib.rs index 618edc33c..d53eee7c2 100644 --- a/crates/terraphim_automata/src/lib.rs +++ b/crates/terraphim_automata/src/lib.rs @@ -344,7 +344,6 @@ pub async fn load_thesaurus_from_json_and_replace_async( /// Note: Remote loading requires the "remote-loading" feature to be enabled. #[cfg(feature = "remote-loading")] pub async fn load_thesaurus(automata_path: &AutomataPath) -> Result { - #[allow(dead_code)] async fn read_url(url: String) -> Result { log::debug!("Reading thesaurus from remote: {url}"); let response = reqwest::Client::builder() @@ -388,11 +387,7 @@ pub async fn load_thesaurus(automata_path: &AutomataPath) -> Result { } fs::read_to_string(path)? } - AutomataPath::Remote(_) => { - return Err(TerraphimAutomataError::InvalidThesaurus( - "Remote loading is not supported. Enable the 'remote-loading' feature.".to_string(), - )); - } + AutomataPath::Remote(url) => read_url(url.clone()).await?, }; let thesaurus = serde_json::from_str(&contents)?; diff --git a/crates/terraphim_rolegraph/Cargo.toml b/crates/terraphim_rolegraph/Cargo.toml index fa60d6337..6ad0c67ee 100644 --- a/crates/terraphim_rolegraph/Cargo.toml +++ b/crates/terraphim_rolegraph/Cargo.toml @@ -20,7 +20,6 @@ aho-corasick = "1.0.2" anyhow = { version = "1.0", optional = true } itertools = "0.14.0" log = "0.4.29" -memoize = "0.5.1" regex = "1.12.3" serde = { version = "1.0.192", features = ["derive"] } serde_json = "1.0.64" diff --git a/crates/terraphim_rolegraph/src/lib.rs b/crates/terraphim_rolegraph/src/lib.rs index 84cde59cf..cefd2c221 100644 --- a/crates/terraphim_rolegraph/src/lib.rs +++ b/crates/terraphim_rolegraph/src/lib.rs @@ -1,6 +1,5 @@ use ahash::AHashMap; use itertools::Itertools; -use memoize::memoize; use regex::Regex; use std::collections::hash_map::Entry; use std::sync::Arc; @@ -1264,8 +1263,7 @@ pub fn split_paragraphs(paragraphs: &str) -> Vec<&str> { /// Combining two numbers into a unique one: pairing functions. /// It uses "elegant pairing" (https://odino.org/combining-two-numbers-into-a-unique-one-pairing-functions/). -/// also using memoize macro with Ahash hasher -#[memoize(CustomHasher: ahash::AHashMap)] +#[inline] pub fn magic_pair(x: u64, y: u64) -> u64 { if x >= y { x * x + x + y } else { y * y + x } } @@ -1279,7 +1277,7 @@ pub fn magic_pair(x: u64, y: u64) -> u64 { // } /// return q, l - q /// } -#[memoize(CustomHasher: ahash::AHashMap)] +#[inline] pub fn magic_unpair(z: u64) -> (u64, u64) { let q = (z as f64).sqrt().floor() as u64; let l = z - q * q; diff --git a/docs/research/2026-03-30-next-pr-selection.md b/docs/research/2026-03-30-next-pr-selection.md new file mode 100644 index 000000000..8b51ba1d1 --- /dev/null +++ b/docs/research/2026-03-30-next-pr-selection.md @@ -0,0 +1,171 @@ +# Research Document: Next PR Selection -- Post 5-PR Merge Sprint + +**Status**: Draft +**Author**: Planning Orchestrator +**Date**: 2026-03-30 +**Reviewers**: Alex + +## Executive Summary + +After merging 5 PRs (#732-#736) into upstream/main, we need to select the best next task. Analysis of 3 conflicting PRs, 8 Gitea ready issues, and 7 code improvement issues reveals that **Gitea #138 (Wire up read_url for remote thesaurus loading)** is the strongest candidate. It is a 1-function fix in a single file, directly builds on our just-merged #733 URL validation fix, and unblocks the entire remote thesaurus loading feature which is currently dead code. + +## Essential Questions Check + +| Question | Answer | Evidence | +|----------|--------|----------| +| Energizing? | Yes | Completes unfinished work from #733; dead code in the codebase is a clear signal | +| Leverages strengths? | Yes | We just fixed the URL validation in the same file -- deep context is fresh | +| Meets real need? | Yes | Remote thesaurus loading is a documented feature that currently silently fails | + +**Proceed**: Yes (3/3 YES) + +## Candidate Analysis + +### Category A: Conflicting PRs Requiring Rebase + +**PR #731 (ADF agent spawning, 10 files, +991/-31)** +- Scope: Large, touches spawner, config, CLI +- Conflict risk: Medium -- depends on what changed in spawner recently +- Effort: 2-4 hours for rebase + testing +- Value: Important but not urgent; agent spawning is experimental + +**PR #726 (terraphim-agent fixes, 17 files, +464/-167)** +- Scope: Medium-large, touches automata, config, middleware +- Conflict risk: HIGH -- directly conflicts with #733 (AutomataPath::from_remote) and #734 (LazyLock) +- Effort: 3-5 hours for rebase + conflict resolution +- Value: High (fixes real user-facing bugs) but conflict resolution is risky + +**PR #426 (RLM orchestration, 106 files, +12544/-200)** +- Scope: Massive, experimental +- Verdict: ELIMINATED -- too large, too risky for single session + +### Category B: Well-Scoped Gitea Issues + +**#138: Wire up read_url for remote thesaurus loading** +- Location: `crates/terraphim_automata/src/lib.rs` lines 346-400 +- Problem: `read_url()` function exists (lines 348-378) but is `#[allow(dead_code)]`. The `Remote` match arm (lines 391-395) returns an error instead of calling `read_url()`. +- Fix: Change `Remote` arm to call `read_url(url.clone()).await?` instead of returning error +- Tests: Existing `test_load_thesaurus_from_url` test (line 449, currently `#[ignore]`) validates this +- Effort: 30 minutes +- Risk: Very low -- the function already exists and was tested before being broken +- Value: HIGH -- unblocks remote thesaurus loading, the primary use case for `AutomataPath::Remote` +- Dependency: Builds directly on #733 (URL validation fix we just merged) + +**#143: Remove unnecessary memoization from magic_pair/magic_unpair** +- Location: `crates/terraphim_rolegraph/src/lib.rs` lines 1268-1287 +- Problem: `magic_pair(x,y)` and `magic_unpair(z)` use `#[memoize(CustomHasher: ahash::AHashMap)]` but the functions are pure arithmetic (2-3 operations). Memoization adds hash table overhead that exceeds computation cost. +- Fix: Remove `#[memoize]` attributes, remove `memoize = "0.5.1"` from Cargo.toml, remove `use memoize::memoize;` import +- Callers: 26 occurrences across 5 files (but all call the same 2 functions) +- Effort: 30 minutes +- Risk: Very low -- pure functions, removing cache can only make behavior more predictable +- Value: Medium -- removes unnecessary dependency, simplifies code, may improve performance for small inputs +- Bonus: Can be combined with #138 in same PR + +**#142: Remove redundant async wrappers in RoleGraph** +- Location: `crates/terraphim_rolegraph/src/lib.rs` +- Problem: `RoleGraph::new()` (line 287) and `from_serializable()` (line 360) are `async fn` that just call sync versions. `RoleGraphSync` methods (lines 1210-1241) are legitimately async (they acquire locks). +- Fix: Could deprecate async wrappers on RoleGraph, but callers use them widely +- Effort: 1-2 hours (need to update all callers across crates) +- Risk: Medium -- changing public API signatures ripples across workspace +- Value: Low -- the wrapper is harmless (zero-cost when optimized) +- Verdict: Defer -- high effort-to-value ratio, breaking API change + +**#141: Improve ID generation for Concept uniqueness per KG** +- Effort: 2-4 hours (design needed) +- Risk: Medium-high -- changes fundamental data model +- Verdict: Defer -- needs its own research phase + +**#140: Reduce cloning in RoleGraph hot paths** +- Effort: 2-3 hours +- Risk: Medium -- needs profiling to identify actual hot paths +- Verdict: Defer -- needs benchmarking first + +**#137: Make TriggerIndex threshold and stopwords configurable** +- Location: `crates/terraphim_rolegraph/src/lib.rs` lines 52-211 +- Problem: Threshold is hardcoded to 0.3, stopwords hardcoded in `is_stopword()` +- Fix: Accept threshold and optional stopword set in constructor, thread through from config +- Effort: 1-2 hours +- Risk: Low -- additive change, backward compatible with defaults +- Value: Medium -- enables tuning for different domains +- Verdict: Good second-tier candidate + +**#135: Clean up dead code and deprecated functions** +- Effort: 1-2 hours (audit + removal) +- Risk: Low but tedious +- Verdict: Could combine with #138 (read_url IS the dead code issue) + +### Category C: High-Priority Gitea Issues (Too Large for One Session) + +**#116, #144, #45, #100, #57**: All require multi-session design and implementation. ELIMINATED from consideration. + +## Recommendation: Combined #138 + #143 + +### Rationale + +1. **#138 is the natural next step** after #733. We fixed `from_remote()` URL validation; now we need to actually wire up the remote loading that was broken. This is a 1-line fix in a function that already exists. + +2. **#143 is a clean dependency removal**. The `memoize` crate adds a runtime hash map cache for 2 arithmetic operations. Removing it simplifies the dependency tree and makes the code more predictable. + +3. **Both changes are orthogonal** -- they touch different crates (`terraphim_automata` vs `terraphim_rolegraph`), so no merge conflicts between them. + +4. **Combined effort: ~1 hour**. Both are well-scoped with existing test coverage. + +5. **Combined value: HIGH**. One unblocks a documented feature. The other removes unnecessary complexity. + +## Constraints + +### Technical Constraints +- #138 requires `remote-loading` feature to be enabled for testing +- #138's live test (`test_load_thesaurus_from_url`) requires network access and is `#[ignore]` +- #143's `memoize` removal needs verification that no other code in the workspace uses the `memoize` crate + +### Vital Few (Max 3) + +| Constraint | Why Vital | Evidence | +|------------|-----------|----------| +| Must not break WASM build | `terraphim_automata` has WASM target; remote-loading is feature-gated | Feature gate already handles this | +| Must keep backward compatibility | `magic_pair`/`magic_unpair` are public API | Removing memoize doesn't change signatures | +| Must have test coverage | Both changes touch core functionality | Existing tests cover both paths | + +### Eliminated from Scope + +| Eliminated Item | Why Eliminated | +|-----------------|----------------| +| PR #426 (RLM) | 12K+ lines, experimental, too large | +| PR #726 rebase | High conflict with #733/#734, 3-5 hours | +| PR #731 rebase | Medium scope but agent spawning is not urgent | +| #142 async wrappers | Breaking API change, low value | +| #141 ID generation | Needs design phase, changes data model | +| #140 reduce cloning | Needs profiling first | +| #116/#144/#45/#100/#57 | Multi-session features, not one-session tasks | + +## Risks and Unknowns + +| Risk | Likelihood | Impact | Mitigation | +|------|------------|--------|------------| +| Remote thesaurus URL in test is stale | Low | Low | Test is `#[ignore]`, can verify manually | +| `memoize` crate used elsewhere in workspace | Low | Low | Grep confirms only in `terraphim_rolegraph` | +| Removing memoize regresses performance for repeated calls | Very Low | Low | Pure math is faster than hash lookup for u64 | + +### Assumptions + +| Assumption | Basis | Risk if Wrong | Verified? | +|------------|-------|---------------|-----------| +| `read_url` function body is correct | It was written intentionally, just not wired up | Would need debugging | Partially (code reads correctly) | +| `reqwest` is available when `remote-loading` feature is enabled | Feature gate in Cargo.toml | Build failure | Yes (feature exists in Cargo.toml) | +| No other workspace crate depends on `memoize` | Grep search | Build failure | Need to verify | + +## Code Locations + +| Component | Location | Purpose | +|-----------|----------|---------| +| `load_thesaurus` (remote) | `crates/terraphim_automata/src/lib.rs:346-400` | Broken Remote arm | +| `read_url` (dead code) | `crates/terraphim_automata/src/lib.rs:348-378` | HTTP fetch for thesaurus | +| `magic_pair` | `crates/terraphim_rolegraph/src/lib.rs:1268-1271` | Memoized pairing function | +| `magic_unpair` | `crates/terraphim_rolegraph/src/lib.rs:1282-1287` | Memoized unpairing function | +| `memoize` import | `crates/terraphim_rolegraph/src/lib.rs:3` | Dependency import | +| `memoize` dep | `crates/terraphim_rolegraph/Cargo.toml:23` | Crate dependency | + +## Next Steps + +If approved, proceed to Phase 2 (Design) for the combined #138 + #143 implementation plan. diff --git a/docs/research/verification-validation-report-issue-538.md b/docs/research/verification-validation-report-issue-538.md new file mode 100644 index 000000000..c93a9ea82 --- /dev/null +++ b/docs/research/verification-validation-report-issue-538.md @@ -0,0 +1,283 @@ +# Verification and Validation Report: Issue #538 + +**Issue**: feat: Add GGUF/llama-cpp backend to terraphim LLM proxy layer +**Repository**: terraphim/terraphim-ai +**Date**: 2026-03-11 +**Status**: NOT IMPLEMENTED + +--- + +## Executive Summary + +Issue #538 proposes adding local GGUF inference capability via `llama-cpp-rs` for CPU-only LLM inference. The current codebase supports remote (OpenRouter) and local (Ollama) providers, but has no direct GGUF/llama.cpp integration. This feature request remains unimplemented. + +| Requirement | Status | Evidence | +|-------------|--------|----------| +| terraphim_llm_local crate exists | NOT MET | No crate found | +| llama-cpp-rs dependency | NOT MET | Not in any Cargo.toml | +| hf-hub dependency | NOT MET | Not in any Cargo.toml | +| CPU-only inference support | NOT MET | No implementation | +| Same trait interface as remote | MET | LlmClient trait exists | + +--- + +## Detailed Verification + +### 1. terraphim_llm_local Crate Exists + +**Status**: NOT MET + +**Evidence**: +```bash +$ ls -la crates/ | grep -E "(llm|local|gguf)" +No matching crates found +``` + +All existing crates in the workspace: +- terraphim_agent_evolution +- terraphim_agent_messaging +- terraphim_agent_registry +- terraphim_agent_supervisor +- terraphim_atomic_client +- terraphim_automata +- terraphim_config +- terraphim_goal_alignment +- terraphim_kg_agents +- terraphim_kg_orchestration +- terraphim_markdown_parser +- terraphim_mcp_server +- terraphim_middleware +- terraphim_multi_agent +- terraphim_onepassword_cli +- terraphim_persistence +- terraphim_rolegraph +- terraphim_router +- terraphim_server +- terraphim_service +- terraphim_settings +- terraphim_spawner +- terraphim_task_decomposition +- terraphim_tinyclaw +- terraphim_types +- terraphim_tui +- haystack_atlassian +- haystack_core +- haystack_discourse +- haystack_jmap + +**No `terraphim_llm_local` crate exists.** + +--- + +### 2. llama-cpp-rs Dependency Present + +**Status**: NOT MET + +**Evidence**: +```bash +$ grep -r "llama-cpp" crates/*/Cargo.toml +No matches found +``` + +**Alternative local inference found**: The codebase uses Ollama for local inference via HTTP API (feature = "ollama"). + +--- + +### 3. hf-hub Dependency Present + +**Status**: NOT MET + +**Evidence**: +```bash +$ grep -r "hf-hub" crates/*/Cargo.toml +No matches found +``` + +No automatic GGUF model download capability exists. + +--- + +### 4. CPU-only Inference Support + +**Status**: NOT MET (for GGUF) + +**Evidence**: +- No llama.cpp integration found +- No GGUF model loading code found +- No quantization variant selection code found + +**Existing local inference**: Ollama integration exists at `crates/terraphim_service/src/llm.rs` (lines 299-564) which provides local inference through Ollama's HTTP API. + +--- + +### 5. Same Trait Interface as Remote + +**Status**: MET + +**Evidence**: +File: `crates/terraphim_service/src/llm.rs` (lines 31-56) + +```rust +#[async_trait::async_trait] +pub trait LlmClient: Send + Sync { + fn name(&self) -> &'static str; + + async fn summarize(&self, content: &str, opts: SummarizeOptions) -> ServiceResult; + + async fn list_models(&self) -> ServiceResult> { + // Default implementation + } + + async fn chat_completion( + &self, + _messages: Vec, + _opts: ChatOptions, + ) -> ServiceResult { + // Default implementation + } +} +``` + +This trait is already implemented by: +- `OpenRouterClient` (lines 243-292) - remote API +- `OllamaClient` (lines 301-564) - local via HTTP +- `RouterBridgeLlmClient` (feature = "llm_router") - routing layer + +A GGUF implementation would use the same trait. + +--- + +## Current LLM Architecture + +``` + +------------------+ + | build_llm_ | + | from_role() | + +--------+---------+ + | + +-----------------+-----------------+ + | | + +------v------+ +------v-------+ + | OpenRouter | | Ollama | + | (remote) | | (local) | + +-------------+ +--------------+ +``` + +**Proposed addition**: +``` + +------------------+ + | build_llm_ | + | from_role() | + +--------+---------+ + | + +-----------------+-----------------+-----------------+ + | | | + +------v------+ +------v-------+ +-----v----------+ + | OpenRouter | | Ollama | | GGUF/Local | + | (remote) | | (local) | | (llama.cpp) | + +-------------+ +--------------+ +---------------+ +``` + +--- + +## Traceability Matrix + +| Requirement | Design Element | Code Location | Test Coverage | Status | +|-------------|----------------|---------------|---------------|--------| +| Common LLM trait | LlmClient trait | llm.rs:31-56 | Unit tests | MET | +| Local inference | OllamaClient | llm.rs:301-564 | Integration tests | PARTIAL | +| GGUF support | MISSING | - | NONE | NOT MET | +| Model download | MISSING | - | NONE | NOT MET | +| Quantization selection | MISSING | - | NONE | NOT MET | + +--- + +## Defect Register + +| ID | Description | Severity | Resolution | Status | +|----|-------------|----------|------------|--------| +| D001 | No terraphim_llm_local crate | High | Create crate with llama-cpp-rs | OPEN | +| D002 | No hf-hub integration | Medium | Add hf-hub dependency for downloads | OPEN | +| D003 | No GGUF quantization selection | Medium | Implement quantization variant selector | OPEN | + +--- + +## Existing Local Inference Alternative + +The codebase currently supports local inference via **Ollama**: + +**Configuration**: +```json +{ + "extra": { + "llm_provider": "ollama", + "ollama_model": "llama3.1", + "ollama_base_url": "http://127.0.0.1:11434" + } +} +``` + +**Limitations vs GGUF proposal**: +- Requires Ollama service running +- No direct GGUF file loading +- No quantization selection at runtime +- Additional dependency (Ollama binary) + +--- + +## Recommendations + +### Option 1: Implement GGUF Support (Full Issue Resolution) + +**Effort**: 2-3 days + +**Steps**: +1. Create `crates/terraphim_llm_local/` crate +2. Add dependencies: `llama-cpp-rs`, `hf-hub` +3. Implement `LlmClient` trait for GGUF models +4. Add model download/caching via hf-hub +5. Implement quantization variant selection +6. Add configuration options to Role.extra +7. Write integration tests + +**Benefits**: +- No Ollama dependency required +- Direct GGUF file support +- Full control over quantization +- Single binary deployment + +### Option 2: Document Ollama as Alternative (Partial Resolution) + +If GGUF implementation is not prioritized, document that Ollama provides local inference capability with GGUF support (Ollama can import and serve GGUF models). + +### Option 3: Close as Not Planned + +If Ollama integration is sufficient for the use case, close this issue with explanation. + +--- + +## Conclusion + +Issue #538 represents a valid feature request that has **not been implemented**. The codebase has the architectural foundation (`LlmClient` trait) to support this feature, but no GGUF/llama.cpp integration exists. + +### GO/NO-GO Decision: NO-GO + +**Reasoning**: +- Feature request is not implemented +- No code exists to validate +- Requires implementation Phase 3 work + +**Next Steps**: +1. If implementing: Create design document for `terraphim_llm_local` crate +2. If not implementing: Close issue with explanation about Ollama alternative +3. If deferring: Add to backlog with priority label + +--- + +## Appendix: Files Referenced + +| File | Path | Purpose | +|------|------|---------| +| LLM trait and providers | `crates/terraphim_service/src/llm.rs` | LlmClient trait, OpenRouter, Ollama impls | +| LLM adapter for evolution | `crates/terraphim_agent_evolution/src/llm_adapter.rs` | Simplified adapter trait | +| Role configuration | `crates/terraphim_config/src/lib.rs` | Role.extra configuration | diff --git a/docs/research/verification-validation-report-issue-8.md b/docs/research/verification-validation-report-issue-8.md new file mode 100644 index 000000000..cde5fa606 --- /dev/null +++ b/docs/research/verification-validation-report-issue-8.md @@ -0,0 +1,283 @@ +# Verification and Validation Report: Issue #8 + +**Issue**: Add OpenAPI documentation to axum server using utoipa +**Repository**: terraphim/terraphim-ai +**Date**: 2026-03-11 +**Status**: NOT IMPLEMENTED + +--- + +## Executive Summary + +Issue #8 (opened 2024-01-07) requests OpenAPI documentation for the axum server using the `utoipa` crate. The server currently has no OpenAPI documentation. While there is a prototype using `poem_openapi` in the lab directory, the main terraphim_server uses axum without any OpenAPI integration. + +| Requirement | Status | Evidence | +|-------------|--------|----------| +| utoipa dependency added | NOT MET | Not in Cargo.toml | +| OpenAPI spec generation | NOT MET | No utoipa attributes | +| API documentation | PARTIAL | Inline docs exist, but no OpenAPI | +| Swagger UI endpoint | NOT MET | Not implemented | + +--- + +## Detailed Verification + +### 1. utoipa Dependency Present + +**Status**: NOT MET + +**Evidence**: +```bash +$ grep -r "utoipa" crates/*/Cargo.toml terraphim_server/Cargo.toml +No matches found +``` + +**Current Dependencies in terraphim_server/Cargo.toml**: +- `axum = { version = "0.8.7", features = ["macros", "ws"] }` - web framework +- `schemars = { version = "0.8.22", optional = true }` - JSON schema generation (feature = "schema") +- No `utoipa` dependency + +--- + +### 2. OpenAPI Spec Generation + +**Status**: NOT MET + +**Evidence**: +File: `terraphim_server/src/api.rs` (lines 1-100) + +The API handlers use standard axum patterns without utoipa attributes: +```rust +pub(crate) async fn create_document( + State(app_state): State, + Json(document): Json, +) -> Result> { + // ... +} + +pub(crate) async fn search_documents( + Extension(_tx): Extension, + State(app_state): State, + search_query: Query, +) -> Result> { + // ... +} +``` + +**Missing utoipa annotations**: +- No `#[utoipa::path()]` attributes on handlers +- No `#[derive(OpenApi)]` on API struct +- No `#[derive(ToSchema)]` on request/response types + +--- + +### 3. API Documentation + +**Status**: PARTIAL + +**Evidence**: + +Inline documentation exists: +```rust +/// Health check endpoint +pub(crate) async fn health() -> impl IntoResponse { + (StatusCode::OK, "OK") +} + +/// Creates index of the document for each rolegraph +pub(crate) async fn create_document(...) -> Result> { + // ... +} + +/// Search for documents in all Terraphim graphs defined in the config via GET params +pub(crate) async fn search_documents(...) -> Result> { + // ... +} +``` + +But no OpenAPI-specific documentation or schema generation. + +--- + +### 4. Swagger UI Endpoint + +**Status**: NOT MET + +**Evidence**: +File: `terraphim_server/src/lib.rs` (router setup) + +No Swagger UI endpoint exists. The server serves embedded frontend assets but no API documentation UI. + +--- + +## Prototype in Lab Directory + +**File**: `lab/parking-lot/server-poem/src/api.rs` + +A prototype exists using `poem_openapi` (different crate than utoipa): +```rust +use poem_openapi::{payload::Json, ApiResponse, OpenApi}; + +#[derive(ApiResponse, Debug, PartialEq, Eq)] +pub enum QueryResponse { + #[oai(status = 200)] + Ok(PlainText), + #[oai(status = 404)] + NotFound, +} + +#[OpenApi] +impl Api { + #[oai(path = "/documents", method = "post", tag = "ApiTags::Document")] + async fn create_document(&self, document: Json) -> CreateDocumentResponse { + // ... + } +} +``` + +This is a **different approach** using the poem framework, not utoipa with axum. + +--- + +## Current API Routes + +Based on `terraphim_server/src/lib.rs`, the following routes exist: + +| Method | Path | Handler | +|--------|------|---------| +| GET | /health | health | +| POST | /documents | create_document | +| POST | /documents/search | search_documents_post | +| GET | /documents/search | search_documents | +| GET | /documents/:id | get_document | +| PUT | /documents/:id | update_document | +| DELETE | /documents/:id | delete_document | +| GET | /config | get_config | +| POST | /config | post_config | +| GET | /roles | list_roles | +| GET | /kg/json/:role | get_rolegraph_json | +| POST | /chat | chat | +| POST | /summarize | summarize | +| GET | /ws | websocket handler | + +--- + +## Alternative: schemars Feature + +The codebase has an optional `schema` feature using `schemars`: + +**Cargo.toml line 78**: +```toml +schema = ["dep:schemars", "terraphim_config/typescript"] +``` + +This generates JSON Schema but NOT OpenAPI. Different standard. + +--- + +## Traceability Matrix + +| Requirement | Design Element | Code Location | Test Coverage | Status | +|-------------|----------------|---------------|---------------|--------| +| utoipa dependency | MISSING | Cargo.toml | N/A | NOT MET | +| OpenApi derive | MISSING | lib.rs | N/A | NOT MET | +| path annotations | MISSING | api.rs | N/A | NOT MET | +| ToSchema derives | MISSING | types | N/A | NOT MET | +| Swagger UI | MISSING | lib.rs | N/A | NOT MET | +| Inline docs | Rust docs | api.rs | N/A | PARTIAL | + +--- + +## Defect Register + +| ID | Description | Severity | Resolution | Status | +|----|-------------|----------|------------|--------| +| D001 | No utoipa dependency | Medium | Add to Cargo.toml | OPEN | +| D002 | No utoipa path annotations | Medium | Add to handlers | OPEN | +| D003 | No ToSchema derives | Medium | Derive for types | OPEN | +| D004 | No OpenApi struct | Medium | Create API spec struct | OPEN | +| D005 | No Swagger UI endpoint | Low | Add /swagger-ui route | OPEN | + +--- + +## Recommendations + +### Option 1: Implement utoipa Integration + +**Effort**: 1-2 days + +**Steps**: +1. Add `utoipa` and `utoipa-swagger-ui` dependencies to terraphim_server/Cargo.toml +2. Add `#[derive(ToSchema)]` to request/response types: + - `Document` + - `SearchQuery` + - `CreateDocumentResponse` + - `SearchResponse` + - etc. +3. Add `#[utoipa::path()]` attributes to handlers +4. Create OpenApi spec struct with `#[derive(OpenApi)]` +5. Add Swagger UI route + +**Example of changes needed**: +```rust +// Before +pub(crate) async fn health() -> impl IntoResponse { + (StatusCode::OK, "OK") +} + +// After +#[utoipa::path( + get, + path = "/health", + responses( + (status = 200, description = "Server is healthy") + ) +)] +pub(crate) async fn health() -> impl IntoResponse { + (StatusCode::OK, "OK") +} +``` + +### Option 2: Use Existing schemars Feature + +If JSON Schema is sufficient (not OpenAPI specifically): +- Document the existing `schema` feature +- Add schema generation endpoint +- Use tools that accept JSON Schema + +### Option 3: Close as Not Planned + +If API documentation is not a current priority: +- Close issue with explanation +- Document the inline Rust docs as the canonical API reference +- Note that the poem prototype in lab/ shows an alternative approach + +--- + +## Conclusion + +Issue #8 represents **unimplemented feature work**. The terraphim_server uses axum with inline documentation but has no OpenAPI integration. A prototype exists using poem_openapi in the lab directory, but this is a different framework than the production axum server. + +### GO/NO-GO Decision: NO-GO + +**Reasoning**: +- Feature request is not implemented +- No utoipa integration exists +- No OpenAPI spec generation +- Prototype uses different framework (poem vs axum) + +**Next Steps**: +1. If implementing: Follow Option 1 steps above +2. If not implementing: Close with explanation about existing schemars feature +3. If deferring: Add to backlog with priority label + +--- + +## Appendix: Files Referenced + +| File | Path | Purpose | +|------|------|---------| +| Server Cargo.toml | `terraphim_server/Cargo.toml` | Dependencies (no utoipa) | +| API handlers | `terraphim_server/src/api.rs` | Axum handlers (no OpenAPI attrs) | +| Server lib | `terraphim_server/src/lib.rs` | Router setup | +| Poem prototype | `lab/parking-lot/server-poem/src/api.rs` | Alternative OpenAPI approach |