diff --git a/crates/adaptive/src/intake/author.rs b/crates/adaptive/src/intake/author.rs index 5759f1e..0e78667 100644 --- a/crates/adaptive/src/intake/author.rs +++ b/crates/adaptive/src/intake/author.rs @@ -199,13 +199,37 @@ fn gated(answer: &Value, facts: &HostFacts, policy: &dyn HostPolicy) -> Result>, + } + + #[async_trait::async_trait] + impl LlmProvider for Forgetful { + async fn complete( + &self, + request: Value, + _conn: Option<&str>, + ) -> tinyflows::error::Result { + let shown = request["messages"][1]["content"] + .as_str() + .unwrap_or_default() + .to_string(); + let mut prompts = self.prompts.lock().expect("prompt log"); + prompts.push(shown.clone()); + let graph = serde_json::json!({ + "schema_version": 1, "name": "needs-topic", + "inputs": [{ "name": "topic", "type": "string", "required": true }], + "nodes": [{ + "id": "start", "kind": "trigger", "name": "manual", + "config": { "trigger_kind": "manual" } + }], + "edges": [] + }); + let inputs = if prompts.len() == 1 { + serde_json::json!({ "extraneous": "trimmed anyway" }) + } else { + assert!( + shown.contains("topic"), + "the retry names the unsupplied input: {shown}" + ); + serde_json::json!({ "topic": "flash models", "extraneous": "still here" }) + }; + Ok(serde_json::json!({ "graph": graph, "why": "test", "inputs": inputs })) + } + } + + #[derive(Debug, Default)] + struct Permissive; + impl HostPolicy for Permissive {} + + let provider = std::sync::Arc::new(Forgetful { + prompts: Mutex::new(Vec::new()), + }); + let caps = Capabilities { + llm: provider.clone(), + ..mock_capabilities() + }; + let attempt = author( + &Goal::new("do the thing"), + &HostFacts::unknown(), + &Permissive, + "", + &caps, + None, + ) + .await + .expect("the corrected inputs must land"); + assert_eq!(attempt.inputs["topic"], "flash models"); + assert!( + !attempt.inputs.contains_key("extraneous"), + "undeclared inputs are trimmed: {:?}", + attempt.inputs + ); + assert_eq!(provider.prompts.lock().expect("prompt log").len(), 2); + } + #[test] fn a_binding_path_inside_prose_is_refused_with_the_remedy() { use tinyflows::model::{Edge, Node, NodeKind};