Skip to main content

newt_core/
ffi_surface.rs

1//! The `knowledge_base` technique (R1) as a memory provider.
2//!
3//! When a profile lists `knowledge_base` (`docs/design/technique-library.md`),
4//! this provider injects the authoritative PyO3 import surface — the FFI manifest
5//! ([`crate::ffi_manifest`]) — into the frozen system prompt, so the model imports
6//! real paths (`newt_agent._newt_agent.core`) instead of guessing `newt_core` from
7//! the crate name (the cross-family confabulation, `docs/findings/`). It rides the
8//! same provider seam as [`crate::AgentsProvider`], so the block survives every
9//! system-prompt rebuild. A **no-op on a non-PyO3 workspace** (empty manifest → no
10//! block) — the technique generalizes safely.
11//!
12//! **Compression role (#661 group E).** Because the surface lives in the frozen
13//! system prompt — the compressor's protected head ([`super::agentic::compress`]
14//! `head_len`) — it is a **stable base that is NEVER summarized away**: the model
15//! keeps an exact import surface even after the middle is compacted, so the lossy
16//! summary has less it must preserve, and an import detail can't be lost to
17//! compression. This is the "inject the authoritative import surface as a stable
18//! base so there's less to summarize" half of the summarizer-effectiveness suite,
19//! verified by `compress::tests::knowledge_base_stable_base_survives_compression`.
20
21use async_trait::async_trait;
22use std::path::Path;
23
24use crate::ffi_manifest::FfiManifest;
25use crate::memory::{MemMessage, MemoryProvider, SessionContext};
26use crate::metrics::TurnMetrics;
27
28/// Injects the workspace's FFI import surface into the system prompt.
29#[derive(Default)]
30pub struct FfiSurfaceProvider {
31    /// The rendered surface block, computed once at [`initialize`](Self::initialize).
32    /// `None` when the workspace has no PyO3 bindings.
33    block: Option<String>,
34}
35
36impl FfiSurfaceProvider {
37    /// A provider that resolves its surface from the session workspace.
38    #[must_use]
39    pub fn new() -> Self {
40        Self::default()
41    }
42}
43
44#[async_trait]
45impl MemoryProvider for FfiSurfaceProvider {
46    fn name(&self) -> &str {
47        "knowledge_base"
48    }
49
50    async fn initialize(&mut self, ctx: &SessionContext) -> anyhow::Result<()> {
51        let manifest = FfiManifest::from_workspace(Path::new(&ctx.workspace)).unwrap_or_default();
52        let n = manifest.len();
53        self.block = if manifest.is_empty() {
54            None
55        } else {
56            Some(manifest.render_block())
57        };
58        if self.block.is_some() {
59            tracing::info!(crates = n, "knowledge_base: FFI import surface injected");
60        }
61        Ok(())
62    }
63
64    fn system_prompt_block(&self) -> Option<String> {
65        self.block.clone()
66    }
67
68    fn build_messages(&self, _system_prompt: &str, _new_task: &str) -> Vec<MemMessage> {
69        // System-prompt-only provider; history is managed elsewhere.
70        Vec::new()
71    }
72
73    async fn sync_turn(&mut self, _user: &str, _assistant: &str, _metrics: &TurnMetrics) {}
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    fn ctx(workspace: &str) -> SessionContext {
81        SessionContext {
82            workspace: workspace.to_string(),
83            session_id: "s".into(),
84        }
85    }
86
87    fn write_binding(root: &Path, krate: &str, submodule: &str) {
88        let dir = root.join(krate).join("src");
89        std::fs::create_dir_all(&dir).unwrap();
90        std::fs::write(
91            dir.join("pyo3_module.rs"),
92            format!("#[pyclass(name = \"X\", module = \"newt_agent._newt_agent.{submodule}\")] struct X;"),
93        )
94        .unwrap();
95    }
96
97    #[tokio::test]
98    async fn injects_surface_for_a_pyo3_workspace() {
99        let dir = tempfile::tempdir().unwrap();
100        write_binding(dir.path(), "newt-core", "core");
101        let mut p = FfiSurfaceProvider::new();
102        p.initialize(&ctx(dir.path().to_str().unwrap()))
103            .await
104            .unwrap();
105        let block = p.system_prompt_block().expect("a surface block");
106        assert!(block.contains("newt_agent._newt_agent.core"));
107        assert!(block.contains("authoritative"));
108    }
109
110    #[tokio::test]
111    async fn no_op_on_non_pyo3_workspace() {
112        let dir = tempfile::tempdir().unwrap();
113        std::fs::write(dir.path().join("README.md"), "no bindings here").unwrap();
114        let mut p = FfiSurfaceProvider::new();
115        p.initialize(&ctx(dir.path().to_str().unwrap()))
116            .await
117            .unwrap();
118        assert!(p.system_prompt_block().is_none(), "should inject nothing");
119    }
120}