diff --git a/src/agent/agent.rs b/src/agent/agent.rs index 6eac718..a4fd81f 100644 --- a/src/agent/agent.rs +++ b/src/agent/agent.rs @@ -7,12 +7,14 @@ //! - **不绑定业务循环**:`submit_turn` 在 `AgentSession` 上,不在 trait 上 use crate::agent::runtime::RuntimeBundle; +#[allow(deprecated)] use crate::llm::types::ToolDefinition; /// Agent 角色抽象。 /// /// 实现此 trait 即可接入 Agent Runtime。典型实现是 struct 持有静态配置(name、system prompt 模板), /// 也可以是基于配置动态生成的轻量实现。 +#[allow(deprecated)] pub trait Agent: Send + Sync { /// 角色名(用于日志、调试、UI 展示)。 fn name(&self) -> &str; diff --git a/src/agent/session.rs b/src/agent/session.rs index 7219c96..1aeb889 100644 --- a/src/agent/session.rs +++ b/src/agent/session.rs @@ -178,6 +178,7 @@ mod tests { use crate::llm::error::LlmError; use crate::llm::hooks::{Hook, HookContext, HookExecutor, HookResult}; use crate::llm::provider::{LlmProvider, ProviderCapabilities, ProviderFeatures}; + use crate::llm::types::message::ContentBlock; use crate::llm::types::request_v2::MessageRequest; use crate::llm::types::response_v2::{MessageResponse, StopReason, StreamEvent}; use crate::tools::ToolRegistry; diff --git a/src/agent/task.rs b/src/agent/task.rs index fab032d..3d4bba8 100644 --- a/src/agent/task.rs +++ b/src/agent/task.rs @@ -10,6 +10,7 @@ //! - 重试由上层新建 `Plan` 实现,`TaskAgent` 不做自动重试 use crate::agent::error::AgentError; +#[allow(deprecated)] use crate::llm::types::ChatResponse; use async_trait::async_trait; @@ -55,6 +56,7 @@ impl Step { /// 均未派生 `Clone`(保留原始错误信息,传递所有权而非克隆)。如需复制 `Plan`, /// 只能 clone 处于 `Pending` / `Running` / `Completed` / `Skipped` 状态的步骤。 #[derive(Debug)] +#[allow(deprecated)] pub enum StepStatus { /// 初始状态 —— 等待执行。 Pending, diff --git a/src/llm/convert.rs b/src/llm/convert.rs index adc4955..727f2e6 100644 --- a/src/llm/convert.rs +++ b/src/llm/convert.rs @@ -40,15 +40,14 @@ pub fn from_openai(msg: &OpenaiChatMessage) -> Message { let mut blocks = content_to_blocks(content); if let Some(calls) = tool_calls { for call in calls { - if let OpenaiToolCall::Function { id, function } = call { - let input: Value = - serde_json::from_str(&function.arguments).unwrap_or(Value::Null); - blocks.push(ContentBlock::ToolUse { - id: id.clone(), - name: function.name.clone(), - input, - }); - } + let OpenaiToolCall::Function { id, function } = call; + let input: Value = + serde_json::from_str(&function.arguments).unwrap_or(Value::Null); + blocks.push(ContentBlock::ToolUse { + id: id.clone(), + name: function.name.clone(), + input, + }); } } Message::Assistant { content: blocks } @@ -254,8 +253,7 @@ pub fn blocks_to_content(blocks: &[ContentBlock]) -> ContentField { #[cfg(test)] mod tests { use super::*; - use crate::llm::types::message::ImageSource; - use crate::llm::types::shared::{AudioFormat, ImageDetail}; + use crate::llm::types::shared::ImageDetail; use serde_json::json; #[test] @@ -364,13 +362,10 @@ mod tests { assert!(matches!(content, ContentField::String(_))); let calls = tool_calls.expect("tool_calls"); assert_eq!(calls.len(), 1); - if let OpenaiToolCall::Function { id, function } = &calls[0] { - assert_eq!(id, "call_1"); - assert_eq!(function.name, "search"); - assert!(function.arguments.contains("rust")); - } else { - panic!("expected Function variant"); - } + let OpenaiToolCall::Function { id, function } = &calls[0]; + assert_eq!(id, "call_1"); + assert_eq!(function.name, "search"); + assert!(function.arguments.contains("rust")); } _ => panic!("expected Assistant"), } diff --git a/src/llm/cycle.rs b/src/llm/cycle.rs index 0ca5fe8..81962c2 100644 --- a/src/llm/cycle.rs +++ b/src/llm/cycle.rs @@ -21,6 +21,7 @@ use crate::llm::stream::StreamEvent; use crate::llm::types::message::{ContentBlock, Message}; use crate::llm::types::request_v2::MessageRequest; use crate::llm::types::response_v2::{MessageResponse, StopReason}; +#[allow(deprecated)] use crate::llm::types::{ToolChoice, ToolDefinition}; /// LLM 调用周期配置。 @@ -79,6 +80,7 @@ pub struct LlmCycle { compact_state: CompactState, } +#[allow(deprecated)] impl LlmCycle { /// 创建一个新的 LlmCycle(持有 `Box` 的独占所有权)。 /// @@ -175,13 +177,10 @@ impl LlmCycle { messages: Vec, tools: Vec, ) -> Result { - let ir_messages: Vec = messages; - let ir_tools: Vec = tools.clone(); - let request = MessageRequest { model: self.config.model.clone(), - messages: ir_messages, - tools: ir_tools, + messages, + tools, tool_choice: ToolChoice::Auto, max_tokens: self.config.max_tokens, temperature: self.config.temperature, @@ -672,6 +671,7 @@ fn truncate_tool_result(s: &str, max_bytes: usize) -> String { #[cfg(test)] mod tests { use super::*; + use crate::llm::provider::{ProviderCapabilities, ProviderFeatures}; use crate::tools::{BaseTool, ToolRegistry}; use async_trait::async_trait; use futures_core::Stream; @@ -858,8 +858,10 @@ mod tests { #[tokio::test] async fn test_submit_with_tools_max_turns_exceeded() { - let mut config = CycleConfig::default(); - config.max_tool_turns = Some(2); + let config = CycleConfig { + max_tool_turns: Some(2), + ..Default::default() + }; let responses = vec![ assistant_tool_call_response(vec![("c1", "add", r#"{"a":1,"b":1}"#)]), assistant_tool_call_response(vec![("c2", "add", r#"{"a":1,"b":1}"#)]), diff --git a/src/llm/hooks.rs b/src/llm/hooks.rs index 7485025..4364a5d 100644 --- a/src/llm/hooks.rs +++ b/src/llm/hooks.rs @@ -75,6 +75,7 @@ impl<'a> HookContext<'a> { } /// 设置 plan step 序号(仅 OnPlanStepComplete 使用,Phase 4b 新增)。 + #[allow(dead_code)] pub(crate) fn with_plan_step_index(mut self, plan_step_index: usize) -> Self { self.plan_step_index = Some(plan_step_index); self diff --git a/src/llm/provider/anthropic.rs b/src/llm/provider/anthropic.rs index bfc6955..cf8a3a1 100644 --- a/src/llm/provider/anthropic.rs +++ b/src/llm/provider/anthropic.rs @@ -36,6 +36,7 @@ const DEFAULT_MAX_TOKENS: u32 = 4096; pub struct AnthropicProvider { http_client: Client, base_url: String, + #[allow(dead_code)] api_key: String, model: String, } @@ -586,6 +587,7 @@ enum AnthropicDelta { struct AnthropicMessageDeltaInner { stop_reason: Option, #[serde(default)] + #[allow(dead_code)] stop_sequence: Option, } @@ -599,6 +601,7 @@ pub struct AnthropicSseStream { chunks: Pin> + Send>>, buffer: String, partial: PartialMessageResponse, + #[allow(dead_code)] next_block_index: u32, saw_terminal: bool, } diff --git a/src/llm/provider/openai.rs b/src/llm/provider/openai.rs index 824b9ef..4d6b12a 100644 --- a/src/llm/provider/openai.rs +++ b/src/llm/provider/openai.rs @@ -720,8 +720,8 @@ impl Stream for ChunkToEventStream { } let data = if let Some(p) = trimmed.strip_prefix("data: ") { p - } else if trimmed.starts_with("data:") { - &trimmed[5..] + } else if let Some(p) = trimmed.strip_prefix("data:") { + p } else { continue; }; diff --git a/src/llm/types/mod.rs b/src/llm/types/mod.rs index a33efea..98e4a2a 100644 --- a/src/llm/types/mod.rs +++ b/src/llm/types/mod.rs @@ -40,6 +40,7 @@ pub use usage::{CompletionTokensDetails, CostTracker, PromptTokensDetails, Usage // Phase 1 起移除 `ChatRequest` 别名 —— 新代码统一使用 `MessageRequest`(v2 IR)。 // `ChatResponse` 结构体仍存在,作为 OpenAI `chat_inner()` 内部 wire-format 转换目标。 /// 旧 wire-format 响应结构(保留用于 OpenAI 内部转换层)。 +#[deprecated(since = "0.1.0", note = "请改用 MessageResponse")] #[derive(Debug, Clone)] pub struct ChatResponse { pub message: OpenaiChatMessage, @@ -47,6 +48,7 @@ pub struct ChatResponse { pub stop_reason: Option, } +#[allow(deprecated)] impl From for ChatResponse { fn from(response: OpenaiChatResponse) -> Self { let message = response @@ -63,6 +65,7 @@ impl From for ChatResponse { } } +#[allow(deprecated)] impl From for OpenaiChatChunk { fn from(response: ChatResponse) -> Self { let delta = Delta::from(response.message.clone()); @@ -95,4 +98,5 @@ impl From for OpenaiChatChunk { } /// 工具定义别名(无新类型冲突,保留)。 +#[deprecated(since = "0.1.0", note = "ToolDefinition 仍直接对应 OpenAI wire-format;未来 v0.2 引入 IR 工具类型后会再次更新")] pub type ToolDefinition = OpenaiToolDefinition; diff --git a/src/memory/conversation.rs b/src/memory/conversation.rs index 9405012..4bfea81 100644 --- a/src/memory/conversation.rs +++ b/src/memory/conversation.rs @@ -236,7 +236,7 @@ mod tests { }; let mut conv = ConversationMemory::new(store, "s1", config); for i in 0..5 { - conv.add_message(Message::user_text(&format!("msg-{i}"))) + conv.add_message(Message::user_text(format!("msg-{i}"))) .await .unwrap(); } @@ -253,7 +253,7 @@ mod tests { }; let mut conv = ConversationMemory::new(store, "s1", config); for i in 0..5 { - conv.add_message(Message::user_text(&format!("msg-{i}"))) + conv.add_message(Message::user_text(format!("msg-{i}"))) .await .unwrap(); } diff --git a/src/memory/knowledge.rs b/src/memory/knowledge.rs index 8fca0cd..de9d96c 100644 --- a/src/memory/knowledge.rs +++ b/src/memory/knowledge.rs @@ -19,7 +19,7 @@ pub const KNOWLEDGE_PREFIX: &str = "knowledge_"; /// 同时维护一个 `Vec` 索引以加速列表遍历。 pub struct KnowledgeStore { store: Arc, - index: std::sync::Mutex>, + index: tokio::sync::Mutex>, } impl KnowledgeStore { @@ -27,7 +27,7 @@ impl KnowledgeStore { pub fn new(store: Arc) -> Self { Self { store, - index: std::sync::Mutex::new(Vec::new()), + index: tokio::sync::Mutex::new(Vec::new()), } } @@ -40,7 +40,7 @@ impl KnowledgeStore { ..Default::default() }) .await?; - let mut index = self.index.lock().unwrap(); + let mut index = self.index.lock().await; index.clear(); for item in items { let page: KnowledgePage = serde_json::from_str(&item.content) @@ -66,7 +66,7 @@ impl KnowledgeStore { created_at: now, }; self.store.save(item).await?; - let mut index = self.index.lock().unwrap(); + let mut index = self.index.lock().await; // 替换或追加 if let Some(existing) = index.iter_mut().find(|e| e.id == page.id) { *existing = PageIndexEntry::from(&page); @@ -106,7 +106,7 @@ impl KnowledgeStore { pub async fn delete_page(&self, id: &str) -> Result<(), MemoryError> { let full_id = format!("{KNOWLEDGE_PREFIX}{id}"); self.store.delete(&full_id).await?; - let mut index = self.index.lock().unwrap(); + let mut index = self.index.lock().await; index.retain(|e| e.id != id); Ok(()) } @@ -120,22 +120,31 @@ impl KnowledgeStore { return Ok(Vec::new()); } let needle = query.to_lowercase(); + // 锁内仅 clone 匹配的 entry id,避免异步 get_page() 持有 index 锁。 + let ids: Vec = { + let index = self.index.lock().await; + index + .iter() + .filter(|entry| { + entry.title.to_lowercase().contains(&needle) + || entry.summary.to_lowercase().contains(&needle) + || entry.tags.iter().any(|t| t.to_lowercase().contains(&needle)) + }) + .map(|entry| entry.id.clone()) + .collect() + }; let mut results = Vec::new(); - let index = self.index.lock().unwrap(); - for entry in index.iter() { - if (entry.title.to_lowercase().contains(&needle) - || entry.summary.to_lowercase().contains(&needle) - || entry.tags.iter().any(|t| t.to_lowercase().contains(&needle))) - && let Some(page) = self.get_page(&entry.id).await? { - results.push(page); - } + for id in &ids { + if let Some(page) = self.get_page(id).await? { + results.push(page); + } } Ok(results) } /// 获取内容目录(所有页面的轻量级索引条目)。 - pub fn get_index(&self) -> Vec { - self.index.lock().unwrap().clone() + pub async fn get_index(&self) -> Vec { + self.index.lock().await.clone() } } @@ -222,7 +231,7 @@ mod tests { let ks = KnowledgeStore::new(store); ks.add_page(make_page("p1", "A", &[])).await.unwrap(); ks.add_page(make_page("p2", "B", &[])).await.unwrap(); - let index = ks.get_index(); + let index = ks.get_index().await; assert_eq!(index.len(), 2); } @@ -233,13 +242,13 @@ mod tests { // 添加页面 ks.add_page(make_page("p1", "A", &[])).await.unwrap(); ks.add_page(make_page("p2", "B", &[])).await.unwrap(); - assert_eq!(ks.get_index().len(), 2); + assert_eq!(ks.get_index().await.len(), 2); // 模拟 index 漂移:清空后重建 - ks.index.lock().unwrap().clear(); - assert_eq!(ks.get_index().len(), 0); + ks.index.lock().await.clear(); + assert_eq!(ks.get_index().await.len(), 0); ks.rebuild_index().await.unwrap(); - assert_eq!(ks.get_index().len(), 2); + assert_eq!(ks.get_index().await.len(), 2); } } diff --git a/src/memory/retriever.rs b/src/memory/retriever.rs index 09fa7bf..da3b280 100644 --- a/src/memory/retriever.rs +++ b/src/memory/retriever.rs @@ -113,9 +113,7 @@ fn extract_keywords(query: &str, stop_words: &HashSet) -> Vec { .split(|c: char| !c.is_alphanumeric()) .filter_map(|s| { let lower = s.to_lowercase(); - if lower.is_empty() || lower.chars().count() < 2 { - None - } else if stop_words.contains(&lower) { + if lower.is_empty() || lower.chars().count() < 2 || stop_words.contains(&lower) { None } else { Some(lower) diff --git a/src/prompt/composer.rs b/src/prompt/composer.rs index e953bd6..ccf2092 100644 --- a/src/prompt/composer.rs +++ b/src/prompt/composer.rs @@ -1,12 +1,11 @@ -use crate::llm::types::openai_message::{ContentField, OpenaiChatMessage, OpenaiContentPart}; -use crate::llm::types::request::OpenaiChatRequest; +use crate::llm::types::message::{ContentBlock, Message}; use crate::prompt::error::PromptError; use crate::prompt::template::{PromptTemplate, TemplateContext}; /// 提示词组合器——构建多角色消息序列。 #[derive(Default)] pub struct PromptComposer { - messages: Vec, + messages: Vec, } impl PromptComposer { @@ -16,7 +15,7 @@ impl PromptComposer { } /// 从已有的消息列表初始化。 - pub fn from_messages(messages: Vec) -> Self { + pub fn from_messages(messages: Vec) -> Self { Self { messages } } @@ -24,34 +23,32 @@ impl PromptComposer { /// 添加一条纯文本 system 消息。 pub fn system(mut self, text: impl Into) -> Self { - self.push_message(OpenaiChatMessage::system_text(text.into())); + self.push_message(Message::system(text.into())); self } /// 添加一条纯文本 user 消息。 pub fn user(mut self, text: impl Into) -> Self { - self.push_message(OpenaiChatMessage::user_text(text.into())); + self.push_message(Message::user_text(text.into())); self } /// 添加一条纯文本 assistant 消息。 pub fn assistant(mut self, text: impl Into) -> Self { - self.push_message(OpenaiChatMessage::assistant_text(text.into())); + self.push_message(Message::assistant(text.into())); self } /// 添加一条纯文本 developer 消息(o1 系列模型使用)。 + /// IR 层统一映射为 `Message::System`,由 Provider 在发送时按目标模型决定 `role`。 pub fn developer(mut self, text: impl Into) -> Self { - self.push_message(OpenaiChatMessage::developer_text(text.into())); + self.push_message(Message::system(text.into())); self } /// 添加一条 Tool 消息(工具执行结果回传)。 pub fn tool(mut self, tool_call_id: impl Into, content: impl Into) -> Self { - self.push_message(OpenaiChatMessage::tool_result( - tool_call_id.into(), - content.into(), - )); + self.push_message(Message::tool_result(tool_call_id.into(), content.into(), false)); self } @@ -64,7 +61,7 @@ impl PromptComposer { ctx: &TemplateContext, ) -> Result { let text = template.render(ctx)?; - self.push_message(OpenaiChatMessage::user_text(text)); + self.push_message(Message::user_text(text)); Ok(self) } @@ -75,7 +72,7 @@ impl PromptComposer { ctx: &TemplateContext, ) -> Result { let text = template.render(ctx)?; - self.push_message(OpenaiChatMessage::system_text(text)); + self.push_message(Message::system(text)); Ok(self) } @@ -86,7 +83,7 @@ impl PromptComposer { ctx: &TemplateContext, ) -> Result { let text = template.render(ctx)?; - self.push_message(OpenaiChatMessage::assistant_text(text)); + self.push_message(Message::assistant(text)); Ok(self) } @@ -97,150 +94,98 @@ impl PromptComposer { ctx: &TemplateContext, ) -> Result { let text = template.render(ctx)?; - self.push_message(OpenaiChatMessage::developer_text(text)); + self.push_message(Message::system(text)); Ok(self) } - // ===== 多模态 ContentPart ===== + // ===== 多模态 ContentBlock ===== - /// 添加一条含指定 ContentPart 的 system 消息。 - pub fn system_content(mut self, part: OpenaiContentPart) -> Self { - self.push_message(OpenaiChatMessage::System { - content: ContentField::Array(vec![part]), - name: None, + /// 添加一条含指定 ContentBlock 的 system 消息。 + pub fn system_content(mut self, block: ContentBlock) -> Self { + self.push_message(Message::System { + content: vec![block], }); self } - /// 添加一条含指定 ContentPart 的 user 消息。 - pub fn user_content(mut self, part: OpenaiContentPart) -> Self { - self.push_message(OpenaiChatMessage::User { - content: ContentField::Array(vec![part]), - name: None, + /// 添加一条含指定 ContentBlock 的 user 消息。 + pub fn user_content(mut self, block: ContentBlock) -> Self { + self.push_message(Message::User { + content: vec![block], }); self } - /// 添加一条含指定 ContentPart 的 assistant 消息。 - pub fn assistant_content(mut self, part: OpenaiContentPart) -> Self { - self.push_message(OpenaiChatMessage::Assistant { - content: ContentField::Array(vec![part]), - refusal: None, - name: None, - tool_calls: None, + /// 添加一条含指定 ContentBlock 的 assistant 消息。 + pub fn assistant_content(mut self, block: ContentBlock) -> Self { + self.push_message(Message::Assistant { + content: vec![block], }); self } - /// 添加一条含指定 ContentPart 的 developer 消息。 - pub fn developer_content(mut self, part: OpenaiContentPart) -> Self { - self.push_message(OpenaiChatMessage::Developer { - content: ContentField::Array(vec![part]), - name: None, + /// 添加一条含指定 ContentBlock 的 developer 消息。 + pub fn developer_content(mut self, block: ContentBlock) -> Self { + self.push_message(Message::System { + content: vec![block], }); self } - /// 添加一条含指定 ContentPart 的 Tool 消息。 + /// 添加一条含指定 ContentBlock 的 Tool 消息。 pub fn tool_content( mut self, tool_call_id: impl Into, - part: OpenaiContentPart, + block: ContentBlock, ) -> Self { - self.push_message(OpenaiChatMessage::Tool { - content: ContentField::Array(vec![part]), + self.push_message(Message::ToolResult { tool_call_id: tool_call_id.into(), + content: vec![block], + is_error: false, }); self } - /// 批量添加 ContentPart 作为 user 消息。 - pub fn user_contents(mut self, parts: Vec) -> Self { - self.push_message(OpenaiChatMessage::User { - content: ContentField::Array(parts), - name: None, - }); + /// 批量添加 ContentBlock 作为 user 消息。 + pub fn user_contents(mut self, blocks: Vec) -> Self { + self.push_message(Message::User { content: blocks }); self } - /// 批量添加 ContentPart 作为 system 消息。 - pub fn system_contents(mut self, parts: Vec) -> Self { - self.push_message(OpenaiChatMessage::System { - content: ContentField::Array(parts), - name: None, - }); + /// 批量添加 ContentBlock 作为 system 消息。 + pub fn system_contents(mut self, blocks: Vec) -> Self { + self.push_message(Message::System { content: blocks }); self } - /// 批量添加 ContentPart 作为 assistant 消息。 - pub fn assistant_contents(mut self, parts: Vec) -> Self { - self.push_message(OpenaiChatMessage::Assistant { - content: ContentField::Array(parts), - refusal: None, - name: None, - tool_calls: None, - }); + /// 批量添加 ContentBlock 作为 assistant 消息。 + pub fn assistant_contents(mut self, blocks: Vec) -> Self { + self.push_message(Message::Assistant { content: blocks }); self } - /// 批量添加 ContentPart 作为 developer 消息。 - pub fn developer_contents(mut self, parts: Vec) -> Self { - self.push_message(OpenaiChatMessage::Developer { - content: ContentField::Array(parts), - name: None, - }); - self - } - - // ===== 角色标识 ===== - - /// 为上一条添加的消息设置 `name` 字段。 - pub fn with_name(mut self, name: impl Into) -> Self { - let name = name.into(); - if let Some(msg) = self.messages.last_mut() { - set_message_name(msg, name); - } + /// 批量添加 ContentBlock 作为 developer 消息。 + pub fn developer_contents(mut self, blocks: Vec) -> Self { + self.push_message(Message::System { content: blocks }); self } // ===== 构建 ===== /// 构建最终的消息列表。 - pub fn build(self) -> Vec { + pub fn build(self) -> Vec { self.messages } - /// 构建并直接创建 ChatRequest(需搭配 model 参数)。 - /// 返回的 `OpenaiChatRequest` 中 `tools`、`temperature`、`max_tokens` 等字段均为 `None`, - /// 可通过结构体更新语法补全:`OpenaiChatRequest { tools: Some(...), ..req }`。 - pub fn build_request(self, model: impl Into) -> OpenaiChatRequest { - OpenaiChatRequest { - model: model.into(), - messages: self.messages, - ..Default::default() - } - } - // ===== 内部方法 ===== - fn push_message(&mut self, msg: OpenaiChatMessage) { + fn push_message(&mut self, msg: Message) { self.messages.push(msg); } } -fn set_message_name(msg: &mut OpenaiChatMessage, name: String) { - match msg { - OpenaiChatMessage::Developer { name: n, .. } => *n = Some(name), - OpenaiChatMessage::System { name: n, .. } => *n = Some(name), - OpenaiChatMessage::User { name: n, .. } => *n = Some(name), - OpenaiChatMessage::Assistant { name: n, .. } => *n = Some(name), - OpenaiChatMessage::Tool { .. } => {} - OpenaiChatMessage::Function { .. } => {} - } -} - -/// 验证消息序列是否符合 OpenAI API 要求。 -pub fn validate_messages(messages: &[OpenaiChatMessage]) -> Result<(), PromptError> { +/// 验证消息序列是否符合 LLM API 要求(Tool 消息必须紧跟含 tool_calls 的 Assistant)。 +pub fn validate_messages(messages: &[Message]) -> Result<(), PromptError> { if messages.is_empty() { return Err(PromptError::InvalidSequence( "消息列表不能为空".to_string(), @@ -251,10 +196,7 @@ pub fn validate_messages(messages: &[OpenaiChatMessage]) -> Result<(), PromptErr for (i, msg) in messages.iter().enumerate() { match msg { - OpenaiChatMessage::Tool { - tool_call_id, - .. - } => { + Message::ToolResult { tool_call_id, .. } => { if last_tool_call_ids.is_empty() { return Err(PromptError::InvalidSequence(format!( "消息[{i}] Tool 消息前必须有 Assistant 消息且含 tool_calls" @@ -267,21 +209,14 @@ pub fn validate_messages(messages: &[OpenaiChatMessage]) -> Result<(), PromptErr ))); } } - OpenaiChatMessage::Assistant { - tool_calls: Some(calls), - .. - } => { + Message::Assistant { content } => { last_tool_call_ids.clear(); - for call in calls { - let crate::llm::types::OpenaiToolCall::Function { id, .. } = call; - last_tool_call_ids.push(id.clone()); + for block in content { + if let ContentBlock::ToolUse { id, .. } = block { + last_tool_call_ids.push(id.clone()); + } } } - OpenaiChatMessage::Assistant { - tool_calls: None, .. - } => { - last_tool_call_ids.clear(); - } _ => { last_tool_call_ids.clear(); } @@ -318,18 +253,18 @@ mod tests { assert_eq!(msgs.len(), 4); match &msgs[3] { - OpenaiChatMessage::Tool { + Message::ToolResult { tool_call_id, content, .. } => { assert_eq!(tool_call_id, "call_123"); - match content { - ContentField::String(s) => assert_eq!(s, "Sunny, 25°C"), - _ => {} + match &content[0] { + ContentBlock::Text { text } => assert_eq!(text, "Sunny, 25°C"), + _ => panic!("Expected Text block"), } } - _ => panic!("Expected Tool message"), + _ => panic!("Expected ToolResult message"), } } @@ -345,7 +280,7 @@ mod tests { #[test] fn test_validate_messages_empty() { - let msgs: Vec = vec![]; + let msgs: Vec = vec![]; assert!(validate_messages(&msgs).is_err()); } diff --git a/src/tools/error.rs b/src/tools/error.rs index 0c546c7..579be63 100644 --- a/src/tools/error.rs +++ b/src/tools/error.rs @@ -105,7 +105,7 @@ mod tests { #[test] fn test_io_is_not_recoverable() { - let io_err = std::io::Error::new(std::io::ErrorKind::Other, "disk"); + let io_err = std::io::Error::other("disk"); let err = ToolError::from(io_err); assert!(!err.is_recoverable()); } diff --git a/src/tools/mcp.rs b/src/tools/mcp.rs index cc68151..1a6f423 100644 --- a/src/tools/mcp.rs +++ b/src/tools/mcp.rs @@ -20,6 +20,7 @@ use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::process::{Child, ChildStdin, ChildStdout, Command}; use tokio::sync::{oneshot, Mutex}; +#[allow(deprecated)] use crate::llm::types::ToolDefinition; use crate::tools::base::{BaseTool, ToolContext, ToolRef}; use crate::tools::error::ToolError; @@ -135,6 +136,7 @@ impl std::fmt::Debug for McpClient { } } +#[allow(deprecated)] impl McpClient { /// 创建一个 MCP 客户端。 pub fn new(server_name: impl Into, transport: McpTransport) -> Self { @@ -540,6 +542,7 @@ enum McpClientHandle { } #[async_trait] +#[allow(deprecated)] impl BaseTool for McpToolAdapter { fn name(&self) -> &str { &self.name diff --git a/src/tools/registry.rs b/src/tools/registry.rs index 9cf1627..aa66b2a 100644 --- a/src/tools/registry.rs +++ b/src/tools/registry.rs @@ -7,6 +7,7 @@ use std::time::Duration; use futures::future::join_all; use serde_json::Value; +#[allow(deprecated)] use crate::llm::types::ToolDefinition; use crate::tools::base::{ToolContext, ToolRef}; use crate::tools::error::ToolError; @@ -70,6 +71,7 @@ impl std::fmt::Debug for ToolRegistry { } } +#[allow(deprecated)] impl ToolRegistry { /// 创建一个新的工具注册表。 pub fn new() -> Self {