修复(LLM目录契约): protocol 缺省改为按 agent_mode 推导
- LlmModelSummary/LlmUnavailableModel 反序列化缺失 protocol 时取 agent_mode 的默认协议,不再一律 openai_responses - 补 cc / anthropic 别名 / 显式 protocol 用例;同步生成绑定注释
This commit is contained in:
+1
-1
@@ -11,6 +11,6 @@ export type LlmModelSummary = { displayName: string, id: string,
|
||||
*/
|
||||
agentMode: AgcAgentMode,
|
||||
/**
|
||||
* 上游线协议;旧服务端缺字段时按 `openai_responses` 兼容。
|
||||
* 上游线协议;旧服务端缺字段时按 `agent_mode` 的默认协议兼容。
|
||||
*/
|
||||
protocol: AgcModelProtocol, };
|
||||
|
||||
@@ -6,4 +6,8 @@ import type { LlmModelUnavailableReason } from "./LlmModelUnavailableReason";
|
||||
/**
|
||||
* 目录里存在、但当前账号不可用的一条模型。字段与 [`LlmModelSummary`] 对齐,另带原因。
|
||||
*/
|
||||
export type LlmUnavailableModel = { displayName: string, id: string, agentMode: AgcAgentMode, protocol: AgcModelProtocol, reason: LlmModelUnavailableReason, };
|
||||
export type LlmUnavailableModel = { displayName: string, id: string, agentMode: AgcAgentMode,
|
||||
/**
|
||||
* 上游线协议;旧服务端缺字段时按 `agent_mode` 的默认协议兼容。
|
||||
*/
|
||||
protocol: AgcModelProtocol, reason: LlmModelUnavailableReason, };
|
||||
|
||||
@@ -124,9 +124,35 @@ impl AgcModelProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
/// 反序列化中间态:`protocol` 缺失时按 `agent_mode` 的默认协议补齐(见 `From` 实现)。
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct LlmModelSummaryWire {
|
||||
display_name: String,
|
||||
id: String,
|
||||
#[serde(default)]
|
||||
agent_mode: AgcAgentMode,
|
||||
#[serde(default)]
|
||||
protocol: Option<AgcModelProtocol>,
|
||||
}
|
||||
|
||||
impl From<LlmModelSummaryWire> for LlmModelSummary {
|
||||
fn from(wire: LlmModelSummaryWire) -> Self {
|
||||
let protocol = wire
|
||||
.protocol
|
||||
.unwrap_or_else(|| wire.agent_mode.default_protocol());
|
||||
Self {
|
||||
display_name: wire.display_name,
|
||||
id: wire.id,
|
||||
agent_mode: wire.agent_mode,
|
||||
protocol,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 本档可用的一条模型摘要。不包含上游真实模型名。
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(rename_all = "camelCase", from = "LlmModelSummaryWire")]
|
||||
#[cfg_attr(feature = "ts-bindings", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "ts-bindings",
|
||||
@@ -144,14 +170,42 @@ pub struct LlmModelSummary {
|
||||
/// 执行器;旧服务端缺字段时按 `codex` 兼容。
|
||||
#[serde(default)]
|
||||
pub agent_mode: AgcAgentMode,
|
||||
/// 上游线协议;旧服务端缺字段时按 `openai_responses` 兼容。
|
||||
/// 上游线协议;旧服务端缺字段时按 `agent_mode` 的默认协议兼容。
|
||||
#[serde(default)]
|
||||
pub protocol: AgcModelProtocol,
|
||||
}
|
||||
|
||||
/// 反序列化中间态:与 [`LlmModelSummaryWire`] 同规则,额外携带 `reason`。
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct LlmUnavailableModelWire {
|
||||
display_name: String,
|
||||
id: String,
|
||||
#[serde(default)]
|
||||
agent_mode: AgcAgentMode,
|
||||
#[serde(default)]
|
||||
protocol: Option<AgcModelProtocol>,
|
||||
reason: LlmModelUnavailableReason,
|
||||
}
|
||||
|
||||
impl From<LlmUnavailableModelWire> for LlmUnavailableModel {
|
||||
fn from(wire: LlmUnavailableModelWire) -> Self {
|
||||
let protocol = wire
|
||||
.protocol
|
||||
.unwrap_or_else(|| wire.agent_mode.default_protocol());
|
||||
Self {
|
||||
display_name: wire.display_name,
|
||||
id: wire.id,
|
||||
agent_mode: wire.agent_mode,
|
||||
protocol,
|
||||
reason: wire.reason,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 目录里存在、但当前账号不可用的一条模型。字段与 [`LlmModelSummary`] 对齐,另带原因。
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(rename_all = "camelCase", from = "LlmUnavailableModelWire")]
|
||||
#[cfg_attr(feature = "ts-bindings", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
feature = "ts-bindings",
|
||||
@@ -168,6 +222,7 @@ pub struct LlmUnavailableModel {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub agent_mode: AgcAgentMode,
|
||||
/// 上游线协议;旧服务端缺字段时按 `agent_mode` 的默认协议兼容。
|
||||
#[serde(default)]
|
||||
pub protocol: AgcModelProtocol,
|
||||
pub reason: LlmModelUnavailableReason,
|
||||
@@ -224,3 +279,45 @@ pub struct LlmModelsResponse {
|
||||
#[cfg_attr(feature = "ts-bindings", ts(type = "number"))]
|
||||
pub revision: u64,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn missing_protocol_follows_agent_mode() {
|
||||
let cc: LlmModelSummary =
|
||||
serde_json::from_str(r#"{"displayName":"Claude","id":"model-a","agentMode":"cc"}"#)
|
||||
.unwrap();
|
||||
assert_eq!(cc.protocol, AgcModelProtocol::Anthropic);
|
||||
|
||||
let legacy_alias: LlmModelSummary = serde_json::from_str(
|
||||
r#"{"displayName":"Claude","id":"model-a","agentMode":"anthropic"}"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(legacy_alias.agent_mode, AgcAgentMode::Cc);
|
||||
assert_eq!(legacy_alias.protocol, AgcModelProtocol::Anthropic);
|
||||
|
||||
let codex: LlmModelSummary =
|
||||
serde_json::from_str(r#"{"displayName":"GPT","id":"model-b"}"#).unwrap();
|
||||
assert_eq!(codex.agent_mode, AgcAgentMode::Codex);
|
||||
assert_eq!(codex.protocol, AgcModelProtocol::OpenAiResponses);
|
||||
|
||||
// 显式 protocol 优先,不被 agent_mode 覆盖。
|
||||
let explicit: LlmModelSummary = serde_json::from_str(
|
||||
r#"{"displayName":"GPT","id":"model-c","agentMode":"codex","protocol":"openai_chat"}"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(explicit.protocol, AgcModelProtocol::OpenAiChat);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unavailable_model_missing_protocol_follows_agent_mode() {
|
||||
let cc: LlmUnavailableModel = serde_json::from_str(
|
||||
r#"{"displayName":"Claude","id":"model-a","agentMode":"cc","reason":"disabled"}"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(cc.protocol, AgcModelProtocol::Anthropic);
|
||||
assert_eq!(cc.reason, LlmModelUnavailableReason::Disabled);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user