refactor(types): ToolChoice 移入 tool.rs

- ToolChoice 枚举与 serde impl 从 types/request.rs 迁入 types/tool.rs
- mod.rs re-export 从 request::ToolChoice 改为 tool::ToolChoice(公共 agcore::llm::types::ToolChoice 路径保持不变)
- request_v2.rs import 路径更新为 crate::llm::types::tool::ToolChoice
- OpenaiChatRequest 字段类型引用更新为 super::tool::ToolChoice(Step 13.1 删除 request.rs 后此临时 import 同步消除)
This commit is contained in:
徐涛
2026-07-08 22:53:46 +08:00
parent 802518b5fe
commit f8df6a9421
4 changed files with 94 additions and 91 deletions
+2 -2
View File
@@ -12,7 +12,7 @@ pub mod usage;
pub use openai_message::{
ContentField, FileData, ImageURL, InputAudio, OpenaiChatMessage, OpenaiContentPart,
};
pub use request::{OpenaiChatRequest, OpenaiTool, StreamOptions, ToolChoice};
pub use request::{OpenaiChatRequest, OpenaiTool, StreamOptions};
pub use request_v2::{ExtraError, MessageRequest, ThinkingConfig};
pub use response::{
Annotation, Choice, ChunkChoice, Delta, Logprobs, OpenaiAudio, OpenaiChatChunk,
@@ -26,7 +26,7 @@ pub use shared::{
AudioFormat, FinishReason, ImageDetail, Modality, ResponseFormat, Role, ServiceTier,
StopSequence,
};
pub use tool::{FunctionCall, OpenaiToolCall, ToolDef};
pub use tool::{FunctionCall, OpenaiToolCall, ToolChoice, ToolDef};
pub use usage::{CompletionTokensDetails, CostTracker, PromptTokensDetails, Usage};
// Re-export IR 内容块 / 消息类型供 `types::ContentBlock` 等历史路径消费。
+1 -88
View File
@@ -1,5 +1,5 @@
use crate::llm::types::shared::{ResponseFormat, ServiceTier, StopSequence};
use crate::llm::types::tool::OpenaiToolDefinition;
use crate::llm::types::tool::{OpenaiToolDefinition, ToolChoice};
use serde::{Deserialize, Serialize};
use serde_json::Value;
@@ -11,93 +11,6 @@ pub struct StreamOptions {
pub include_obfuscation: Option<bool>,
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub enum ToolChoice {
#[default]
None,
Auto,
Required,
Named {
name: String,
},
AllowedTools {
tool_names: Vec<String>,
},
}
impl Serialize for ToolChoice {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
ToolChoice::None => serializer.serialize_str("none"),
ToolChoice::Auto => serializer.serialize_str("auto"),
ToolChoice::Required => serializer.serialize_str("required"),
ToolChoice::Named { name } => {
let obj = serde_json::json!({
"type": "function",
"function": { "name": name }
});
obj.serialize(serializer)
}
ToolChoice::AllowedTools { tool_names } => {
let obj = serde_json::json!({
"type": "function",
"function": { "name": tool_names.first().cloned().unwrap_or_default() }
});
obj.serialize(serializer)
}
}
}
}
impl<'de> Deserialize<'de> for ToolChoice {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = Value::deserialize(deserializer)?;
match value {
Value::String(s) => match s.as_str() {
"none" => Ok(ToolChoice::None),
"auto" => Ok(ToolChoice::Auto),
"required" => Ok(ToolChoice::Required),
_ => Err(serde::de::Error::custom(format!(
"unknown tool choice: {s}"
))),
},
Value::Object(obj) => {
let typ = obj.get("type").and_then(|v| v.as_str()).ok_or_else(|| {
serde::de::Error::custom("missing 'type' field in tool_choice")
})?;
if typ == "function" {
let func =
obj.get("function")
.and_then(|v| v.as_object())
.ok_or_else(|| {
serde::de::Error::custom("missing 'function' field in tool_choice")
})?;
let name = func.get("name").and_then(|v| v.as_str()).ok_or_else(|| {
serde::de::Error::custom("missing 'function.name' in tool_choice")
})?;
Ok(ToolChoice::Named {
name: name.to_string(),
})
} else {
Err(serde::de::Error::custom(format!(
"unknown tool_choice type: {typ}"
)))
}
}
_ => Err(serde::de::Error::custom(
"tool_choice must be a string or object",
)),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum OpenaiTool {
+1 -1
View File
@@ -9,7 +9,7 @@ use serde_json::Value;
use thiserror::Error;
use crate::llm::types::message::Message;
use crate::llm::types::request::ToolChoice;
use crate::llm::types::tool::ToolChoice;
use crate::llm::types::tool::ToolDef;
/// Provider 无关的请求类型。
+90
View File
@@ -63,3 +63,93 @@ pub struct FunctionCall {
pub enum OpenaiToolCall {
Function { id: String, function: FunctionCall },
}
/// 工具选择策略 —— Phase 13 从 `types::request::ToolChoice` 迁入。
///
/// `#[non_exhaustive]` 预留扩展空间。
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub enum ToolChoice {
#[default]
None,
Auto,
Required,
Named {
name: String,
},
AllowedTools {
tool_names: Vec<String>,
},
}
impl Serialize for ToolChoice {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
ToolChoice::None => serializer.serialize_str("none"),
ToolChoice::Auto => serializer.serialize_str("auto"),
ToolChoice::Required => serializer.serialize_str("required"),
ToolChoice::Named { name } => {
let obj = serde_json::json!({
"type": "function",
"function": { "name": name }
});
obj.serialize(serializer)
}
ToolChoice::AllowedTools { tool_names } => {
let obj = serde_json::json!({
"type": "function",
"function": { "name": tool_names.first().cloned().unwrap_or_default() }
});
obj.serialize(serializer)
}
}
}
}
impl<'de> Deserialize<'de> for ToolChoice {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = Value::deserialize(deserializer)?;
match value {
Value::String(s) => match s.as_str() {
"none" => Ok(ToolChoice::None),
"auto" => Ok(ToolChoice::Auto),
"required" => Ok(ToolChoice::Required),
_ => Err(serde::de::Error::custom(format!(
"unknown tool choice: {s}"
))),
},
Value::Object(obj) => {
let typ = obj.get("type").and_then(|v| v.as_str()).ok_or_else(|| {
serde::de::Error::custom("missing 'type' field in tool_choice")
})?;
if typ == "function" {
let func =
obj.get("function")
.and_then(|v| v.as_object())
.ok_or_else(|| {
serde::de::Error::custom("missing 'function' field in tool_choice")
})?;
let name = func.get("name").and_then(|v| v.as_str()).ok_or_else(|| {
serde::de::Error::custom("missing 'function.name' in tool_choice")
})?;
Ok(ToolChoice::Named {
name: name.to_string(),
})
} else {
Err(serde::de::Error::custom(format!(
"unknown tool_choice type: {typ}"
)))
}
}
_ => Err(serde::de::Error::custom(
"tool_choice must be a string or object",
)),
}
}
}