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:
@@ -12,7 +12,7 @@ pub mod usage;
|
|||||||
pub use openai_message::{
|
pub use openai_message::{
|
||||||
ContentField, FileData, ImageURL, InputAudio, OpenaiChatMessage, OpenaiContentPart,
|
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 request_v2::{ExtraError, MessageRequest, ThinkingConfig};
|
||||||
pub use response::{
|
pub use response::{
|
||||||
Annotation, Choice, ChunkChoice, Delta, Logprobs, OpenaiAudio, OpenaiChatChunk,
|
Annotation, Choice, ChunkChoice, Delta, Logprobs, OpenaiAudio, OpenaiChatChunk,
|
||||||
@@ -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, ToolDef};
|
pub use tool::{FunctionCall, OpenaiToolCall, ToolChoice, 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` 等历史路径消费。
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::llm::types::shared::{ResponseFormat, ServiceTier, StopSequence};
|
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::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
@@ -11,93 +11,6 @@ pub struct StreamOptions {
|
|||||||
pub include_obfuscation: Option<bool>,
|
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)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "snake_case", tag = "type")]
|
#[serde(rename_all = "snake_case", tag = "type")]
|
||||||
pub enum OpenaiTool {
|
pub enum OpenaiTool {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use serde_json::Value;
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::llm::types::message::Message;
|
use crate::llm::types::message::Message;
|
||||||
use crate::llm::types::request::ToolChoice;
|
use crate::llm::types::tool::ToolChoice;
|
||||||
use crate::llm::types::tool::ToolDef;
|
use crate::llm::types::tool::ToolDef;
|
||||||
|
|
||||||
/// Provider 无关的请求类型。
|
/// Provider 无关的请求类型。
|
||||||
|
|||||||
@@ -63,3 +63,93 @@ pub struct FunctionCall {
|
|||||||
pub enum OpenaiToolCall {
|
pub enum OpenaiToolCall {
|
||||||
Function { id: String, function: FunctionCall },
|
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",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user