修复真实验收协议名解析
仅允许空值默认 openai_responses 显式校验 openai_responses、openai_chat 和 anthropic 为未知协议名补充失败用例并同步验收说明
This commit is contained in:
@@ -88,5 +88,5 @@ Responses 如果只发送 `response.completed` 或 `response.incomplete`,解
|
||||
## 9. 验收证据边界
|
||||
|
||||
1. `cargo test -p platform-llm` 的确定性用例把 checked-in SSE fixture 交给 parser,验证归一后的文本、工具调用、slot 聚合、Responses 仅有 completed / incomplete 终态事件时的恢复、参数 JSON 完整性和错误边界。fixture 可以来源于真实端点抓包,但测试不保存原始 SSE,也不逐事件与端点报文比较,因此不能证明抓包转录无偏差。
|
||||
2. `tests/live_stream_tool_calls.rs` 是默认 `#[ignore]` 的真实端点工具调用 smoke。它只验证最终归一结果中的工具名、id 和完整参数 JSON;文本增量字符数仅用于打印观测,工具调用不进入 `on_delta`,也没有原始 SSE 录制或逐事件对比能力。
|
||||
2. `tests/live_stream_tool_calls.rs` 是默认 `#[ignore]` 的真实端点工具调用 smoke。`PLATFORM_LLM_LIVE_API_KIND` 支持 `openai_responses`、`openai_chat` 和 `anthropic`,未设置或空白时默认 `openai_responses`,未知非空值会直接使验收失败。它只验证最终归一结果中的工具名、id 和完整参数 JSON;文本增量字符数仅用于打印观测,工具调用不进入 `on_delta`,也没有原始 SSE 录制或逐事件对比能力。
|
||||
3. 因此验收应分别称为“固定 SSE fixture parser 覆盖”和“真实端点归一工具调用 smoke”,不能把后者描述为原始 SSE fidelity 或转录一致性证明。
|
||||
|
||||
@@ -26,11 +26,58 @@ fn env_var(name: &str) -> Option<String> {
|
||||
.filter(|value| !value.trim().is_empty())
|
||||
}
|
||||
|
||||
fn parse_api_kind(value: &str) -> LlmApiKind {
|
||||
match value.trim().to_ascii_lowercase().replace('-', "_").as_str() {
|
||||
"anthropic" => LlmApiKind::Anthropic,
|
||||
"openai_chat" => LlmApiKind::OpenAiChat,
|
||||
_ => LlmApiKind::OpenAiResponses,
|
||||
fn parse_api_kind(value: &str) -> Result<LlmApiKind, String> {
|
||||
let normalized = value.trim().to_ascii_lowercase().replace('-', "_");
|
||||
if normalized.is_empty() {
|
||||
return Ok(LlmApiKind::OpenAiResponses);
|
||||
}
|
||||
|
||||
match normalized.as_str() {
|
||||
"anthropic" => Ok(LlmApiKind::Anthropic),
|
||||
"openai_chat" => Ok(LlmApiKind::OpenAiChat),
|
||||
"openai_responses" => Ok(LlmApiKind::OpenAiResponses),
|
||||
value => Err(format!(
|
||||
"PLATFORM_LLM_LIVE_API_KIND 无效:{value},请使用 openai_responses、openai_chat 或 anthropic"
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_api_kind_defaults_only_for_empty_value() {
|
||||
assert_eq!(
|
||||
parse_api_kind("").expect("empty api kind should default"),
|
||||
LlmApiKind::OpenAiResponses
|
||||
);
|
||||
assert_eq!(
|
||||
parse_api_kind(" ").expect("whitespace api kind should default"),
|
||||
LlmApiKind::OpenAiResponses
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_api_kind_accepts_supported_values() {
|
||||
assert_eq!(
|
||||
parse_api_kind("anthropic").expect("anthropic should parse"),
|
||||
LlmApiKind::Anthropic
|
||||
);
|
||||
assert_eq!(
|
||||
parse_api_kind("OPENAI-CHAT").expect("openai chat should parse"),
|
||||
LlmApiKind::OpenAiChat
|
||||
);
|
||||
assert_eq!(
|
||||
parse_api_kind("openai_responses").expect("openai responses should parse"),
|
||||
LlmApiKind::OpenAiResponses
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_api_kind_rejects_unknown_non_empty_value() {
|
||||
let error = parse_api_kind("anthopic").expect_err("misspelled api kind must fail");
|
||||
assert!(error.contains("anthopic"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +91,12 @@ async fn live_stream_run_returns_native_tool_calls() {
|
||||
) else {
|
||||
panic!("缺少 PLATFORM_LLM_LIVE_BASE_URL / _API_KEY / _MODEL");
|
||||
};
|
||||
let api_kind = parse_api_kind(&env_var("PLATFORM_LLM_LIVE_API_KIND").unwrap_or_default());
|
||||
let api_kind = parse_api_kind(
|
||||
env_var("PLATFORM_LLM_LIVE_API_KIND")
|
||||
.as_deref()
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.unwrap_or_else(|error| panic!("{error}"));
|
||||
|
||||
let config = LlmConfig::new(
|
||||
LlmProvider::OpenAiCompatible,
|
||||
|
||||
Reference in New Issue
Block a user