refactor(core): 扫清 v0.1 Phase A 技术债
- 修复测试编译回归:补全 session.rs/cycle.rs 测试模块导入; convert.rs 2 处 irrefutable if let 改为 let - composer.rs 迁移至 IR:OpenaiChatMessage → Message, ContentField/OpenaiContentPart → ContentBlock;删除 set_message_name 和 build_request;developer 消息映射为 Message::System - knowledge.rs 锁修复:std::sync::Mutex → tokio::sync::Mutex; search() 优化锁粒度(锁内仅 clone IDs,避免锁内异步 IO) - 标记 ChatResponse / ToolDefinition 为废弃(#[deprecated(since = "0.1.0")]), 内部使用点加 #[allow(deprecated)] 抑制警告 - clippy 清零:合并冗余 if、手动 strip_prefix 改 strip_prefix、 多处 dead_code 抑制、测试代码清理
This commit is contained in:
+63
-128
@@ -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<OpenaiChatMessage>,
|
||||
messages: Vec<Message>,
|
||||
}
|
||||
|
||||
impl PromptComposer {
|
||||
@@ -16,7 +15,7 @@ impl PromptComposer {
|
||||
}
|
||||
|
||||
/// 从已有的消息列表初始化。
|
||||
pub fn from_messages(messages: Vec<OpenaiChatMessage>) -> Self {
|
||||
pub fn from_messages(messages: Vec<Message>) -> Self {
|
||||
Self { messages }
|
||||
}
|
||||
|
||||
@@ -24,34 +23,32 @@ impl PromptComposer {
|
||||
|
||||
/// 添加一条纯文本 system 消息。
|
||||
pub fn system(mut self, text: impl Into<String>) -> 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<String>) -> 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<String>) -> 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<String>) -> 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<String>, content: impl Into<String>) -> 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<Self, PromptError> {
|
||||
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<Self, PromptError> {
|
||||
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<Self, PromptError> {
|
||||
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<Self, PromptError> {
|
||||
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<String>,
|
||||
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<OpenaiContentPart>) -> Self {
|
||||
self.push_message(OpenaiChatMessage::User {
|
||||
content: ContentField::Array(parts),
|
||||
name: None,
|
||||
});
|
||||
/// 批量添加 ContentBlock 作为 user 消息。
|
||||
pub fn user_contents(mut self, blocks: Vec<ContentBlock>) -> Self {
|
||||
self.push_message(Message::User { content: blocks });
|
||||
self
|
||||
}
|
||||
|
||||
/// 批量添加 ContentPart 作为 system 消息。
|
||||
pub fn system_contents(mut self, parts: Vec<OpenaiContentPart>) -> Self {
|
||||
self.push_message(OpenaiChatMessage::System {
|
||||
content: ContentField::Array(parts),
|
||||
name: None,
|
||||
});
|
||||
/// 批量添加 ContentBlock 作为 system 消息。
|
||||
pub fn system_contents(mut self, blocks: Vec<ContentBlock>) -> Self {
|
||||
self.push_message(Message::System { content: blocks });
|
||||
self
|
||||
}
|
||||
|
||||
/// 批量添加 ContentPart 作为 assistant 消息。
|
||||
pub fn assistant_contents(mut self, parts: Vec<OpenaiContentPart>) -> 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<ContentBlock>) -> Self {
|
||||
self.push_message(Message::Assistant { content: blocks });
|
||||
self
|
||||
}
|
||||
|
||||
/// 批量添加 ContentPart 作为 developer 消息。
|
||||
pub fn developer_contents(mut self, parts: Vec<OpenaiContentPart>) -> Self {
|
||||
self.push_message(OpenaiChatMessage::Developer {
|
||||
content: ContentField::Array(parts),
|
||||
name: None,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
// ===== 角色标识 =====
|
||||
|
||||
/// 为上一条添加的消息设置 `name` 字段。
|
||||
pub fn with_name(mut self, name: impl Into<String>) -> 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<ContentBlock>) -> Self {
|
||||
self.push_message(Message::System { content: blocks });
|
||||
self
|
||||
}
|
||||
|
||||
// ===== 构建 =====
|
||||
|
||||
/// 构建最终的消息列表。
|
||||
pub fn build(self) -> Vec<OpenaiChatMessage> {
|
||||
pub fn build(self) -> Vec<Message> {
|
||||
self.messages
|
||||
}
|
||||
|
||||
/// 构建并直接创建 ChatRequest(需搭配 model 参数)。
|
||||
/// 返回的 `OpenaiChatRequest` 中 `tools`、`temperature`、`max_tokens` 等字段均为 `None`,
|
||||
/// 可通过结构体更新语法补全:`OpenaiChatRequest { tools: Some(...), ..req }`。
|
||||
pub fn build_request(self, model: impl Into<String>) -> 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<OpenaiChatMessage> = vec![];
|
||||
let msgs: Vec<Message> = vec![];
|
||||
assert!(validate_messages(&msgs).is_err());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user