refactor(llm): 移除 ToolDefinition 别名与遗留 deprecation 抑制点

完成 ToolDef IR 切换的最后清理:移除 ToolDefinition 别名与
OpenaiToolDefinition 的公共 re-export;各调用点(cycle/registry/mcp/agent)
直接使用 ToolDef 并清理对应的 #[allow(deprecated)] 抑制点;新增
roundtrip 测试验证 ToolDef 序列化兼容性。
This commit is contained in:
徐涛
2026-07-05 10:32:35 +08:00
parent 9da9b83167
commit 4cf5918b9c
6 changed files with 57 additions and 27 deletions
+2 -4
View File
@@ -7,14 +7,12 @@
//! - **不绑定业务循环**`submit_turn` 在 `AgentSession` 上,不在 trait 上 //! - **不绑定业务循环**`submit_turn` 在 `AgentSession` 上,不在 trait 上
use crate::agent::runtime::RuntimeBundle; use crate::agent::runtime::RuntimeBundle;
#[allow(deprecated)] use crate::llm::types::tool::ToolDef;
use crate::llm::types::ToolDefinition;
/// Agent 角色抽象。 /// Agent 角色抽象。
/// ///
/// 实现此 trait 即可接入 Agent Runtime。典型实现是 struct 持有静态配置(name、system prompt 模板), /// 实现此 trait 即可接入 Agent Runtime。典型实现是 struct 持有静态配置(name、system prompt 模板),
/// 也可以是基于配置动态生成的轻量实现。 /// 也可以是基于配置动态生成的轻量实现。
#[allow(deprecated)]
pub trait Agent: Send + Sync { pub trait Agent: Send + Sync {
/// 角色名(用于日志、调试、UI 展示)。 /// 角色名(用于日志、调试、UI 展示)。
fn name(&self) -> &str; fn name(&self) -> &str;
@@ -26,7 +24,7 @@ pub trait Agent: Send + Sync {
/// ///
/// **默认实现**:从 `bundle.tool_registry` 取全部工具(最常用模式)。 /// **默认实现**:从 `bundle.tool_registry` 取全部工具(最常用模式)。
/// **子 trait / 具体实现可覆盖**:做白名单、过滤、按状态动态调整等。 /// **子 trait / 具体实现可覆盖**:做白名单、过滤、按状态动态调整等。
fn tool_definitions(&self, bundle: &RuntimeBundle) -> Vec<ToolDefinition> { fn tool_definitions(&self, bundle: &RuntimeBundle) -> Vec<ToolDef> {
bundle.tool_registry.definitions() bundle.tool_registry.definitions()
} }
} }
+7 -7
View File
@@ -21,8 +21,8 @@ use crate::llm::stream::StreamEvent;
use crate::llm::types::message::{ContentBlock, Message}; use crate::llm::types::message::{ContentBlock, Message};
use crate::llm::types::request_v2::MessageRequest; use crate::llm::types::request_v2::MessageRequest;
use crate::llm::types::response_v2::{MessageResponse, StopReason}; use crate::llm::types::response_v2::{MessageResponse, StopReason};
#[allow(deprecated)] use crate::llm::types::tool::ToolDef;
use crate::llm::types::{ToolChoice, ToolDefinition}; use crate::llm::types::ToolChoice;
/// LLM 调用周期配置。 /// LLM 调用周期配置。
pub struct CycleConfig { pub struct CycleConfig {
@@ -179,7 +179,7 @@ impl LlmCycle {
pub async fn submit_messages( pub async fn submit_messages(
&mut self, &mut self,
messages: Vec<Message>, messages: Vec<Message>,
tools: Vec<ToolDefinition>, tools: Vec<ToolDef>,
) -> Result<MessageResponse, LlmError> { ) -> Result<MessageResponse, LlmError> {
let request = MessageRequest { let request = MessageRequest {
model: self.config.model.clone(), model: self.config.model.clone(),
@@ -237,7 +237,7 @@ impl LlmCycle {
pub async fn submit( pub async fn submit(
&mut self, &mut self,
prompt: String, prompt: String,
tools: Vec<ToolDefinition>, tools: Vec<ToolDef>,
) -> Result<MessageResponse, LlmError> { ) -> Result<MessageResponse, LlmError> {
self.messages.push(Message::user_text(prompt)); self.messages.push(Message::user_text(prompt));
@@ -347,7 +347,7 @@ impl LlmCycle {
pub async fn submit_stream( pub async fn submit_stream(
&mut self, &mut self,
prompt: String, prompt: String,
tools: Vec<ToolDefinition>, tools: Vec<ToolDef>,
) -> Result<Pin<Box<dyn Stream<Item = StreamEvent> + Send>>, LlmError> { ) -> Result<Pin<Box<dyn Stream<Item = StreamEvent> + Send>>, LlmError> {
self.messages.push(Message::user_text(prompt)); self.messages.push(Message::user_text(prompt));
@@ -429,7 +429,7 @@ impl LlmCycle {
})) }))
} }
fn build_request(&self, tools: &[ToolDefinition]) -> MessageRequest { fn build_request(&self, tools: &[ToolDef]) -> MessageRequest {
// ponytail: Phase 2 简化 —— 直接 clone self.messages,无任何转换 / system prompt 注入。 // ponytail: Phase 2 简化 —— 直接 clone self.messages,无任何转换 / system prompt 注入。
// 系统消息如需存在,由调用方通过 `with_messages()` 自行管理。 // 系统消息如需存在,由调用方通过 `with_messages()` 自行管理。
MessageRequest { MessageRequest {
@@ -448,7 +448,7 @@ impl LlmCycle {
/// 用于 `submit_with_tools()` 的多轮 tool 循环。 /// 用于 `submit_with_tools()` 的多轮 tool 循环。
async fn submit_request( async fn submit_request(
&mut self, &mut self,
tools: &[ToolDefinition], tools: &[ToolDef],
) -> Result<MessageResponse, LlmError> { ) -> Result<MessageResponse, LlmError> {
let mut attempts = 0; let mut attempts = 0;
+1 -4
View File
@@ -26,7 +26,7 @@ pub use shared::{
AudioFormat, FinishReason, ImageDetail, Modality, ResponseFormat, Role, ServiceTier, AudioFormat, FinishReason, ImageDetail, Modality, ResponseFormat, Role, ServiceTier,
StopSequence, StopSequence,
}; };
pub use tool::{FunctionCall, OpenaiToolCall, OpenaiToolDefinition, ToolDef}; pub use tool::{FunctionCall, OpenaiToolCall, ToolDef};
pub use usage::{CompletionTokensDetails, CostTracker, PromptTokensDetails, Usage}; pub use usage::{CompletionTokensDetails, CostTracker, PromptTokensDetails, Usage};
// Re-export IR 内容块 / 消息类型供 `types::ContentBlock` 等历史路径消费。 // Re-export IR 内容块 / 消息类型供 `types::ContentBlock` 等历史路径消费。
@@ -96,6 +96,3 @@ impl From<ChatResponse> for OpenaiChatChunk {
} }
} }
} }
/// 工具定义别名 —— 指向 v0.2 新增的 Provider 无关 IR 类型。
pub type ToolDefinition = ToolDef;
+40
View File
@@ -198,4 +198,44 @@ mod tests {
assert_eq!(decoded.stream, req.stream); assert_eq!(decoded.stream, req.stream);
assert_eq!(decoded.extra.get("trace_id"), Some(&json!("t-1"))); assert_eq!(decoded.extra.get("trace_id"), Some(&json!("t-1")));
} }
#[test]
fn message_request_with_tools_roundtrip() {
// 验证 ToolDef 的 serde 属性与 OpenaiToolDefinition 一致:
// 同名字段(name/description/parameters)序列化结果应一致。
let params = json!({
"type": "object",
"properties": {"x": {"type": "number"}},
"required": ["x"],
});
let tool = super::ToolDef {
name: "add".to_string(),
description: Some("add two numbers".to_string()),
parameters: params.clone(),
};
let req = MessageRequest {
model: "gpt-4o".into(),
messages: vec![Message::user_text("hi")],
tools: vec![tool],
tool_choice: ToolChoice::Auto,
max_tokens: None,
temperature: None,
top_p: None,
stop_sequences: vec![],
stream: false,
thinking: None,
extra: HashMap::new(),
};
let json = serde_json::to_string(&req).expect("serialize");
// 验证反序列化能还原所有字段(包括嵌套 parameters
let decoded: MessageRequest = serde_json::from_str(&json).expect("deserialize");
assert_eq!(decoded.tools.len(), 1);
assert_eq!(decoded.tools[0].name, "add");
assert_eq!(decoded.tools[0].description.as_deref(), Some("add two numbers"));
assert_eq!(decoded.tools[0].parameters, params);
// 验证序列化 JSON 不含 ToolDef 没有的字段(如 strict),保持 wire-format 兼容
assert!(!json.contains("strict"), "ToolDef 序列化不应包含 strict 字段");
}
} }
+3 -6
View File
@@ -20,8 +20,7 @@ use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::process::{Child, ChildStdin, ChildStdout, Command}; use tokio::process::{Child, ChildStdin, ChildStdout, Command};
use tokio::sync::{Mutex, oneshot}; use tokio::sync::{Mutex, oneshot};
#[allow(deprecated)] use crate::llm::types::tool::ToolDef;
use crate::llm::types::ToolDefinition;
use crate::tools::base::{BaseTool, ToolContext, ToolRef}; use crate::tools::base::{BaseTool, ToolContext, ToolRef};
use crate::tools::error::ToolError; use crate::tools::error::ToolError;
@@ -136,7 +135,6 @@ impl std::fmt::Debug for McpClient {
} }
} }
#[allow(deprecated)]
impl McpClient { impl McpClient {
/// 创建一个 MCP 客户端。 /// 创建一个 MCP 客户端。
pub fn new(server_name: impl Into<String>, transport: McpTransport) -> Self { pub fn new(server_name: impl Into<String>, transport: McpTransport) -> Self {
@@ -237,7 +235,7 @@ impl McpClient {
} }
/// 列出服务器支持的工具(调用 `tools/list`)。 /// 列出服务器支持的工具(调用 `tools/list`)。
pub async fn list_tools(&mut self) -> Result<Vec<ToolDefinition>, ToolError> { pub async fn list_tools(&mut self) -> Result<Vec<ToolDef>, ToolError> {
if !self.is_initialized() { if !self.is_initialized() {
return Err(ToolError::McpNotInitialized(self.server_name.clone())); return Err(ToolError::McpNotInitialized(self.server_name.clone()));
} }
@@ -272,7 +270,7 @@ impl McpClient {
description: description.clone(), description: description.clone(),
input_schema: input_schema.clone(), input_schema: input_schema.clone(),
}); });
defs.push(ToolDefinition { defs.push(ToolDef {
name, name,
description, description,
parameters: input_schema, parameters: input_schema,
@@ -530,7 +528,6 @@ enum McpClientHandle {
} }
#[async_trait] #[async_trait]
#[allow(deprecated)]
impl BaseTool for McpToolAdapter { impl BaseTool for McpToolAdapter {
fn name(&self) -> &str { fn name(&self) -> &str {
&self.name &self.name
+4 -6
View File
@@ -7,8 +7,7 @@ use std::time::Duration;
use futures::future::join_all; use futures::future::join_all;
use serde_json::Value; use serde_json::Value;
#[allow(deprecated)] use crate::llm::types::tool::ToolDef;
use crate::llm::types::ToolDefinition;
use crate::tools::base::{ToolContext, ToolRef}; use crate::tools::base::{ToolContext, ToolRef};
use crate::tools::error::ToolError; use crate::tools::error::ToolError;
use crate::tools::permission::PermissionChecker; use crate::tools::permission::PermissionChecker;
@@ -71,7 +70,6 @@ impl std::fmt::Debug for ToolRegistry {
} }
} }
#[allow(deprecated)]
impl ToolRegistry { impl ToolRegistry {
/// 创建一个新的工具注册表。 /// 创建一个新的工具注册表。
pub fn new() -> Self { pub fn new() -> Self {
@@ -127,12 +125,12 @@ impl ToolRegistry {
self.inner.tools.keys().cloned().collect() self.inner.tools.keys().cloned().collect()
} }
/// 获取所有工具的 `ToolDefinition` 列表(用于传递给 LLM)。 /// 获取所有工具的 `ToolDef` 列表(用于传递给 LLM)。
pub fn definitions(&self) -> Vec<ToolDefinition> { pub fn definitions(&self) -> Vec<ToolDef> {
self.inner self.inner
.tools .tools
.values() .values()
.map(|tool| ToolDefinition { .map(|tool| ToolDef {
name: tool.name().to_string(), name: tool.name().to_string(),
description: Some(tool.description().to_string()), description: Some(tool.description().to_string()),
parameters: tool.parameters(), parameters: tool.parameters(),