From 51e632c997767dc0811b507a58358c82f78031d4 Mon Sep 17 00:00:00 2001 From: ProofOfConcept Date: Sat, 4 Apr 2026 16:39:04 -0400 Subject: [PATCH] tools: delete ToolDef and FunctionDef ToolDef and FunctionDef are gone. Tool definitions are static strings on the Tool struct. The API layer builds JSON from Tool::to_json(). - ChatRequest.tools is now Option - start_stream takes &[Tool] instead of Option<&[ToolDef]> - openai::stream_events takes &serde_json::Value for tools - memory_and_journal_tools() returns Vec for subconscious agents - Subconscious agents filter by t.name instead of t.function.name No more runtime JSON construction for tool definitions. No more ToolDef::new(). No more FunctionDef. Co-Authored-By: Proof of Concept --- src/agent/api/mod.rs | 17 +++++++--- src/agent/api/openai.rs | 8 ++--- src/agent/api/types.rs | 4 +-- src/agent/mod.rs | 3 +- src/agent/tools/mod.rs | 62 ++++------------------------------- src/subconscious/api.rs | 9 +++-- src/subconscious/knowledge.rs | 8 ++--- 7 files changed, 33 insertions(+), 78 deletions(-) diff --git a/src/agent/api/mod.rs b/src/agent/api/mod.rs index d2c415f..9d398fc 100644 --- a/src/agent/api/mod.rs +++ b/src/agent/api/mod.rs @@ -17,7 +17,7 @@ use std::time::{Duration, Instant}; use tokio::sync::mpsc; -use crate::agent::tools::{ToolCall, ToolDef, FunctionCall}; +use crate::agent::tools::{self as agent_tools, ToolCall, FunctionCall}; use crate::user::ui_channel::{UiMessage, UiSender}; /// A JoinHandle that aborts its task when dropped. @@ -41,6 +41,12 @@ pub struct SamplingParams { // Stream events — yielded by backends, consumed by the runner // ───────────────────────────────────────────────────────────── +/// Build the tools JSON string from a slice of Tools. +fn tools_to_json_str(tools: &[agent_tools::Tool]) -> String { + let inner: Vec = tools.iter().map(|t| t.to_json()).collect(); + format!("[{}]", inner.join(",")) +} + /// Events produced by the streaming API backends. /// The runner reads these and decides what to display where. pub enum StreamEvent { @@ -98,7 +104,7 @@ impl ApiClient { pub fn start_stream( &self, messages: &[Message], - tools: Option<&[ToolDef]>, + tools: &[agent_tools::Tool], ui_tx: &UiSender, reasoning_effort: &str, sampling: SamplingParams, @@ -109,7 +115,8 @@ impl ApiClient { let api_key = self.api_key.clone(); let model = self.model.clone(); let messages = messages.to_vec(); - let tools = tools.map(|t| t.to_vec()); + let tools_json = tools_to_json_str(tools); + let tools_value: serde_json::Value = serde_json::from_str(&tools_json).unwrap_or_default(); let ui_tx = ui_tx.clone(); let reasoning_effort = reasoning_effort.to_string(); let base_url = self.base_url.clone(); @@ -117,7 +124,7 @@ impl ApiClient { let handle = tokio::spawn(async move { let result = openai::stream_events( &client, &base_url, &api_key, &model, - &messages, tools.as_deref(), &tx, &ui_tx, + &messages, &tools_value, &tx, &ui_tx, &reasoning_effort, sampling, priority, ).await; if let Err(e) = result { @@ -131,7 +138,7 @@ impl ApiClient { pub async fn chat_completion_stream_temp( &self, messages: &[Message], - tools: Option<&[ToolDef]>, + tools: &[agent_tools::Tool], ui_tx: &UiSender, reasoning_effort: &str, sampling: SamplingParams, diff --git a/src/agent/api/openai.rs b/src/agent/api/openai.rs index 7b380a2..0956e4d 100644 --- a/src/agent/api/openai.rs +++ b/src/agent/api/openai.rs @@ -8,7 +8,6 @@ use anyhow::Result; use reqwest::Client; use tokio::sync::mpsc; -use crate::agent::tools::ToolDef; use super::types::*; use crate::user::ui_channel::{UiMessage, UiSender}; use super::StreamEvent; @@ -22,18 +21,19 @@ pub(super) async fn stream_events( api_key: &str, model: &str, messages: &[Message], - tools: Option<&[ToolDef]>, + tools_json: &serde_json::Value, tx: &mpsc::UnboundedSender, ui_tx: &UiSender, reasoning_effort: &str, sampling: super::SamplingParams, priority: Option, ) -> Result<()> { + let has_tools = tools_json.as_array().map_or(false, |a| !a.is_empty()); let request = ChatRequest { model: model.to_string(), messages: messages.to_vec(), - tool_choice: tools.map(|_| "auto".to_string()), - tools: tools.map(|t| t.to_vec()), + tool_choice: if has_tools { Some("auto".to_string()) } else { None }, + tools: if has_tools { Some(tools_json.clone()) } else { None }, max_tokens: Some(16384), temperature: Some(sampling.temperature), top_p: Some(sampling.top_p), diff --git a/src/agent/api/types.rs b/src/agent/api/types.rs index 0961fd6..ed80303 100644 --- a/src/agent/api/types.rs +++ b/src/agent/api/types.rs @@ -9,7 +9,7 @@ use chrono::Utc; use serde::{Deserialize, Serialize}; -use crate::agent::tools::{ToolCall, ToolCallDelta, ToolDef}; +use crate::agent::tools::{ToolCall, ToolCallDelta}; /// Message content — either plain text or an array of content parts /// (for multimodal messages with images). Serializes as a JSON string @@ -87,7 +87,7 @@ pub struct ChatRequest { pub model: String, pub messages: Vec, #[serde(skip_serializing_if = "Option::is_none")] - pub tools: Option>, + pub tools: Option, #[serde(skip_serializing_if = "Option::is_none")] pub tool_choice: Option, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/src/agent/mod.rs b/src/agent/mod.rs index fe96e6c..5927c6c 100644 --- a/src/agent/mod.rs +++ b/src/agent/mod.rs @@ -306,10 +306,9 @@ impl Agent { top_p: me.top_p, top_k: me.top_k, }; - let tool_defs: Vec<_> = me.tools.iter().map(|t| t.to_tool_def()).collect(); me.client.start_stream( &api_messages, - Some(&tool_defs), + &me.tools, ui_tx, &me.reasoning_effort, sampling, diff --git a/src/agent/tools/mod.rs b/src/agent/tools/mod.rs index 5d75995..16cafb0 100644 --- a/src/agent/tools/mod.rs +++ b/src/agent/tools/mod.rs @@ -18,7 +18,7 @@ mod write; // Agent-specific tools mod control; mod vision; -pub mod working_stack; +mod working_stack; use serde::{Serialize, Deserialize}; use std::future::Future; @@ -55,19 +55,6 @@ impl Tool { self.parameters_json, ) } - - /// Build a ToolDef (for backward compat where ToolDef is still used). - pub fn to_tool_def(&self) -> ToolDef { - ToolDef { - tool_type: "function".to_string(), - function: FunctionDef { - name: self.name.to_string(), - description: self.description.to_string(), - parameters: serde_json::from_str(self.parameters_json) - .expect("invalid JSON in tool parameters"), - }, - } - } } /// Function call within a tool call — name + JSON arguments. @@ -77,15 +64,6 @@ pub struct FunctionCall { pub arguments: String, } -/// Function definition for tool schema. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct FunctionDef { - pub name: String, - pub description: String, - pub parameters: serde_json::Value, -} - - /// Partial function call within a streaming delta. #[derive(Debug, Deserialize)] pub struct FunctionCallDelta { @@ -93,27 +71,6 @@ pub struct FunctionCallDelta { pub arguments: Option, } -/// Tool definition sent to the model. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ToolDef { - #[serde(rename = "type")] - pub tool_type: String, - pub function: FunctionDef, -} - -impl ToolDef { - pub fn new(name: &str, description: &str, parameters: serde_json::Value) -> Self { - Self { - tool_type: "function".to_string(), - function: FunctionDef { - name: name.to_string(), - description: description.to_string(), - parameters, - }, - } - } -} - /// A tool call requested by the model. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ToolCall { @@ -135,7 +92,6 @@ pub struct ToolCallDelta { pub function: Option, } - /// A tool call in flight — metadata for TUI + JoinHandle for /// result collection and cancellation. pub struct ActiveToolCall { @@ -220,17 +176,11 @@ pub fn tools() -> Vec { all } -/// Return all tool definitions for the API. -pub fn definitions() -> Vec { - tools().into_iter().map(|t| t.to_tool_def()).collect() -} - -/// Return memory + journal tool definitions only. -pub fn memory_and_journal_definitions() -> Vec { - memory::memory_tools().into_iter() - .chain(memory::journal_tools()) - .map(|t| t.to_tool_def()) - .collect() +/// Memory + journal tools only — for subconscious agents. +pub fn memory_and_journal_tools() -> Vec { + let mut all = memory::memory_tools().to_vec(); + all.extend(memory::journal_tools()); + all } /// Create a short summary of tool args for the tools pane header. diff --git a/src/subconscious/api.rs b/src/subconscious/api.rs index 38ceece..864c5dc 100644 --- a/src/subconscious/api.rs +++ b/src/subconscious/api.rs @@ -46,13 +46,12 @@ pub async fn call_api_with_tools( let (ui_tx, mut ui_rx) = crate::user::ui_channel::channel(); // All available native tools for subconscious agents - let all_tools = agent_tools::memory_and_journal_definitions(); - // If agent header specifies a tools whitelist, filter to only those - let tool_defs: Vec<_> = if tools.is_empty() { + let all_tools = agent_tools::memory_and_journal_tools(); + let agent_tool_list: Vec<_> = if tools.is_empty() { all_tools } else { all_tools.into_iter() - .filter(|t| tools.iter().any(|w| w == &t.function.name)) + .filter(|t| tools.iter().any(|w| *w == t.name)) .collect() }; // Provenance tracks which agent:phase is making writes. @@ -83,7 +82,7 @@ pub async fn call_api_with_tools( }; match client.chat_completion_stream_temp( &messages, - Some(&tool_defs), + &agent_tool_list, &ui_tx, &reasoning, sampling, diff --git a/src/subconscious/knowledge.rs b/src/subconscious/knowledge.rs index e89c637..21529af 100644 --- a/src/subconscious/knowledge.rs +++ b/src/subconscious/knowledge.rs @@ -295,13 +295,13 @@ fn run_one_agent_inner( _llm_tag: &str, log: &(dyn Fn(&str) + Sync), ) -> Result { - let all_tools = crate::agent::tools::memory_and_journal_definitions(); + let all_tools = crate::agent::tools::memory_and_journal_tools(); let effective_tools: Vec = if def.tools.is_empty() { - all_tools.iter().map(|t| t.function.name.clone()).collect() + all_tools.iter().map(|t| t.name.to_string()).collect() } else { all_tools.iter() - .filter(|t| def.tools.iter().any(|w| w == &t.function.name)) - .map(|t| t.function.name.clone()) + .filter(|t| def.tools.iter().any(|w| w == &t.name)) + .map(|t| t.name.to_string()) .collect() }; let tools_desc = effective_tools.join(", ");