feat(llm): 重构 types.rs 为完整的 OpenAI 兼容类型系统

将 `types.rs` 拆分为模块化目录,所有类型派生 `Serialize/Deserialize`,
并新增 `OpenaiChatChunk`、`Role` 扩展等 30+ 缺失类型
消除对 `cycle/usage.rs` 的反向依赖,`Usage`/`CostTracker` 移至 `types/usage.rs`
This commit is contained in:
徐涛
2026-05-13 20:51:05 +08:00
parent 54387a5f64
commit a4b7b3b9f9
12 changed files with 882 additions and 453 deletions
+32 -44
View File
@@ -7,19 +7,15 @@ pub use usage::{CostTracker, Usage};
use crate::llm::cycle::retry::should_retry;
use crate::llm::error::LlmError;
use crate::llm::provider::LlmProvider;
use crate::llm::types::{ChatRequest, ChatResponse, ContentBlock, Message, Role, ToolDefinition};
use crate::llm::types::{
ChatRequest, ChatResponse, OpenaiChatMessage, OpenaiTool, ToolChoice, ToolDefinition,
};
/// LLM 生命周期引擎的配置。
pub struct CycleConfig {
/// 使用的模型名称。
pub model: String,
/// 最大输出 token 数。
pub max_tokens: Option<u32>,
/// 采样温度。
pub temperature: Option<f32>,
/// 最大对话轮数(预留,暂未使用)。
pub max_turns: Option<u32>,
/// 重试策略配置。
pub retry: RetryConfig,
}
@@ -35,22 +31,15 @@ impl Default for CycleConfig {
}
}
/// LLM 调用生命周期引擎。
///
/// 管理一次多轮交互的完整生命周期,包括:
/// - 消息历史维护
/// - Token 用量追踪
/// - 自动重试
pub struct LlmCycle {
provider: Box<dyn LlmProvider>,
config: CycleConfig,
usage: CostTracker,
messages: Vec<Message>,
messages: Vec<OpenaiChatMessage>,
system_prompt: Option<String>,
}
impl LlmCycle {
/// 创建新的生命周期引擎。
pub fn new(provider: Box<dyn LlmProvider>, config: CycleConfig) -> Self {
Self {
provider,
@@ -61,50 +50,33 @@ impl LlmCycle {
}
}
/// 设置系统提示词(Builder 模式)。
pub fn with_system_prompt(mut self, prompt: String) -> Self {
self.system_prompt = Some(prompt);
self
}
/// 获取 Token 用量追踪器引用。
pub fn usage(&self) -> &CostTracker {
&self.usage
}
/// 获取当前消息历史。
pub fn messages(&self) -> &[Message] {
pub fn messages(&self) -> &[OpenaiChatMessage] {
&self.messages
}
/// 清空消息历史。
pub fn clear_messages(&mut self) {
self.messages.clear();
}
/// 重置 Token 用量统计。
pub fn reset_usage(&mut self) {
self.usage.reset();
}
/// 提交一条用户消息并获取模型响应。
///
/// 流程:
/// 1. 将用户消息追加到消息历史
/// 2. 构建 ChatRequest
/// 3. 使用重试循环调用 provider.chat()
/// 4. 将助手回复追加到消息历史
/// 5. 累计 token 用量
/// 6. 返回 ChatResponse
pub async fn submit(
&mut self,
prompt: String,
tools: Vec<ToolDefinition>,
) -> Result<ChatResponse, LlmError> {
self.messages.push(Message {
role: Role::User,
content: vec![ContentBlock::Text { text: prompt }],
});
self.messages.push(OpenaiChatMessage::user_text(prompt));
let mut attempts = 0;
@@ -113,10 +85,7 @@ impl LlmCycle {
match self.provider.chat(request).await {
Ok(response) => {
self.messages.push(Message {
role: Role::Assistant,
content: response.message.content.clone(),
});
self.messages.push(response.message.clone());
self.usage.add(&response.usage);
@@ -134,16 +103,35 @@ impl LlmCycle {
}
}
/// 根据当前状态构建 ChatRequest。
fn build_request(&self, tools: &[ToolDefinition]) -> ChatRequest {
let mut messages = self.messages.clone();
if let Some(sys_prompt) = &self.system_prompt
&& !messages.iter().any(|m| matches!(m, OpenaiChatMessage::System { .. }))
{
messages.insert(0, OpenaiChatMessage::system_text(sys_prompt));
}
let openai_tools: Option<Vec<OpenaiTool>> = if tools.is_empty() {
None
} else {
Some(
tools.iter()
.map(|t| OpenaiTool::Function {
function: t.clone(),
})
.collect(),
)
};
ChatRequest {
model: self.config.model.clone(),
messages: self.messages.clone(),
system_prompt: self.system_prompt.clone(),
tools: tools.to_vec(),
messages,
max_tokens: self.config.max_tokens,
temperature: self.config.temperature,
extra_body: None,
tools: openai_tools,
tool_choice: Some(ToolChoice::Auto),
..Default::default()
}
}
}
}