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<serde_json::Value> - start_stream takes &[Tool] instead of Option<&[ToolDef]> - openai::stream_events takes &serde_json::Value for tools - memory_and_journal_tools() returns Vec<Tool> 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 <poc@bcachefs.org>
This commit is contained in:
parent
d195160b1e
commit
51e632c997
7 changed files with 33 additions and 78 deletions
|
|
@ -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<String> = 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,
|
||||
|
|
|
|||
|
|
@ -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<StreamEvent>,
|
||||
ui_tx: &UiSender,
|
||||
reasoning_effort: &str,
|
||||
sampling: super::SamplingParams,
|
||||
priority: Option<i32>,
|
||||
) -> 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),
|
||||
|
|
|
|||
|
|
@ -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<Message>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tools: Option<Vec<ToolDef>>,
|
||||
pub tools: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tool_choice: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<String>,
|
||||
}
|
||||
|
||||
/// 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<FunctionCallDelta>,
|
||||
}
|
||||
|
||||
|
||||
/// 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<Tool> {
|
|||
all
|
||||
}
|
||||
|
||||
/// Return all tool definitions for the API.
|
||||
pub fn definitions() -> Vec<ToolDef> {
|
||||
tools().into_iter().map(|t| t.to_tool_def()).collect()
|
||||
}
|
||||
|
||||
/// Return memory + journal tool definitions only.
|
||||
pub fn memory_and_journal_definitions() -> Vec<ToolDef> {
|
||||
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<Tool> {
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -295,13 +295,13 @@ fn run_one_agent_inner(
|
|||
_llm_tag: &str,
|
||||
log: &(dyn Fn(&str) + Sync),
|
||||
) -> Result<AgentResult, String> {
|
||||
let all_tools = crate::agent::tools::memory_and_journal_definitions();
|
||||
let all_tools = crate::agent::tools::memory_and_journal_tools();
|
||||
let effective_tools: Vec<String> = 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(", ");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue