重构: tool_registry拆分及多批改进
This commit is contained in:
@@ -451,6 +451,107 @@ impl AnthropicCompatProvider {
|
||||
messages.push(serde_json::json!({ "role": "user", "content": blocks }));
|
||||
}
|
||||
|
||||
/// 生成 messages 诊断摘要(每条 role + content 形态 + tool 标记),不含敏感数据。
|
||||
/// B-260618-27: 1214 类错误时随 bail 文案直达前端 raw,定位哪条/字段非法。
|
||||
fn summarize_messages(messages: &[serde_json::Value]) -> String {
|
||||
let lines: Vec<String> = messages
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, m)| {
|
||||
let role = m.get("role").and_then(|r| r.as_str()).unwrap_or("?");
|
||||
let desc = match m.get("content") {
|
||||
Some(serde_json::Value::String(s)) => format!("text({}B)", s.len()),
|
||||
Some(serde_json::Value::Array(blocks)) => {
|
||||
let parts: Vec<String> = blocks
|
||||
.iter()
|
||||
.map(|b| {
|
||||
let ty = b.get("type").and_then(|t| t.as_str()).unwrap_or("?");
|
||||
match ty {
|
||||
"text" => format!(
|
||||
"text({}B)",
|
||||
b.get("text").and_then(|t| t.as_str()).map(|s| s.len()).unwrap_or(0)
|
||||
),
|
||||
"tool_use" => format!(
|
||||
"tool_use[id={},input_obj={}]",
|
||||
b.get("id").and_then(|t| t.as_str()).unwrap_or(""),
|
||||
b.get("input").map(|v| v.is_object()).unwrap_or(false)
|
||||
),
|
||||
"tool_result" => format!(
|
||||
"tool_result[tid={}]",
|
||||
b.get("tool_use_id").and_then(|t| t.as_str()).unwrap_or("")
|
||||
),
|
||||
"image" => "image".to_string(),
|
||||
_ => ty.to_string(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
format!("[{}]", parts.join(","))
|
||||
}
|
||||
_ => "?".to_string(),
|
||||
};
|
||||
format!("#{}:{} {}", i, role, desc)
|
||||
})
|
||||
.collect();
|
||||
format!("{} msgs: {}", lines.len(), lines.join(" | "))
|
||||
}
|
||||
|
||||
/// B-260618-27: 协议预检——扫 messages 发现确定非法形态,命中返回原因(仅诊断不修复)。
|
||||
/// 覆盖:首条非 user / 连续同 role / tool_use input 非 object / 空 content / orphan tool_result
|
||||
/// (tool_use_id 无前置 tool_use,常见于裁剪/过滤后 assistant 被删但 tool_result 留)。
|
||||
fn precheck_messages(messages: &[serde_json::Value]) -> Result<(), String> {
|
||||
if messages.is_empty() {
|
||||
return Err("messages 为空".into());
|
||||
}
|
||||
let first_role = messages[0].get("role").and_then(|r| r.as_str()).unwrap_or("");
|
||||
if first_role != "user" {
|
||||
return Err(format!("首条 role={} 非法(须 user)", first_role));
|
||||
}
|
||||
for w in messages.windows(2) {
|
||||
let r0 = w[0].get("role").and_then(|r| r.as_str()).unwrap_or("");
|
||||
let r1 = w[1].get("role").and_then(|r| r.as_str()).unwrap_or("");
|
||||
if r0 == r1 && (r0 == "user" || r0 == "assistant") {
|
||||
return Err(format!("连续同 role={}", r0));
|
||||
}
|
||||
}
|
||||
let mut tool_use_ids: Vec<&str> = Vec::new();
|
||||
for (i, m) in messages.iter().enumerate() {
|
||||
let role = m.get("role").and_then(|r| r.as_str()).unwrap_or("");
|
||||
match m.get("content") {
|
||||
Some(serde_json::Value::String(s)) if s.is_empty() && role == "user" => {
|
||||
return Err(format!("#{} user content 空", i));
|
||||
}
|
||||
Some(serde_json::Value::Array(blocks)) => {
|
||||
if blocks.is_empty() && role == "user" {
|
||||
return Err(format!("#{} user content 空数组", i));
|
||||
}
|
||||
for b in blocks {
|
||||
match b.get("type").and_then(|t| t.as_str()).unwrap_or("") {
|
||||
"tool_use" => {
|
||||
let id = b.get("id").and_then(|t| t.as_str()).unwrap_or("");
|
||||
tool_use_ids.push(id);
|
||||
if !b.get("input").map(|v| v.is_object()).unwrap_or(false) {
|
||||
return Err(format!("#{} tool_use input 非 object", i));
|
||||
}
|
||||
}
|
||||
"tool_result" => {
|
||||
let tid = b.get("tool_use_id").and_then(|t| t.as_str()).unwrap_or("");
|
||||
if !tid.is_empty() && !tool_use_ids.contains(&tid) {
|
||||
return Err(format!(
|
||||
"#{} orphan tool_result(tid={} 无前置 tool_use)",
|
||||
i, tid
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 统一鉴权头:x-api-key + anthropic-version
|
||||
fn auth_headers(&self, rb: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
|
||||
rb.header("x-api-key", &self.api_key)
|
||||
@@ -466,6 +567,13 @@ impl LlmProvider for AnthropicCompatProvider {
|
||||
req.stream = false;
|
||||
let body = self.convert_request(req);
|
||||
|
||||
// B-260618-27: 协议预检——命中非法 bail 含 messages 摘要,把 GLM 模糊 1214 转明确诊断
|
||||
if let Err(reason) = Self::precheck_messages(&body.messages) {
|
||||
let summary = Self::summarize_messages(&body.messages);
|
||||
warn!(%reason, %summary, "Anthropic messages 协议预检失败");
|
||||
anyhow::bail!("messages 预检失败: {} | 摘要: {}", reason, summary);
|
||||
}
|
||||
|
||||
debug!(model = %body.model, "Anthropic 同步调用");
|
||||
|
||||
// 指数退避重试(B-260616-07): 包裹 send + 状态码判定。
|
||||
@@ -504,7 +612,14 @@ impl LlmProvider for AnthropicCompatProvider {
|
||||
if is_reqwest_error_retryable(&e) {
|
||||
return AttemptOutcome::Retryable(format!("请求失败(可重试): {}", e));
|
||||
}
|
||||
return AttemptOutcome::Fatal(format!("请求失败(不可重试): {}", e));
|
||||
return AttemptOutcome::Fatal(format!(
|
||||
"请求失败(不可重试): {} [timeout={} connect={} body={} src={:?}]",
|
||||
e,
|
||||
e.is_timeout(),
|
||||
e.is_connect(),
|
||||
e.is_body(),
|
||||
std::error::Error::source(&e)
|
||||
));
|
||||
}
|
||||
};
|
||||
if !resp.status().is_success() {
|
||||
@@ -575,6 +690,13 @@ impl LlmProvider for AnthropicCompatProvider {
|
||||
req.stream = true;
|
||||
let body = self.convert_request(req);
|
||||
|
||||
// B-260618-27: 协议预检——命中非法 bail 含 messages 摘要,把 GLM 模糊 1214 转明确诊断
|
||||
if let Err(reason) = Self::precheck_messages(&body.messages) {
|
||||
let summary = Self::summarize_messages(&body.messages);
|
||||
warn!(%reason, %summary, "Anthropic messages 协议预检失败");
|
||||
anyhow::bail!("messages 预检失败: {} | 摘要: {}", reason, summary);
|
||||
}
|
||||
|
||||
debug!(model = %body.model, "Anthropic 流式调用");
|
||||
|
||||
let resp = match self
|
||||
@@ -597,7 +719,15 @@ impl LlmProvider for AnthropicCompatProvider {
|
||||
source = ?std::error::Error::source(&e),
|
||||
"Anthropic 流式 send 失败"
|
||||
);
|
||||
anyhow::bail!("Anthropic 流式 send 失败: {}", e);
|
||||
anyhow::bail!(
|
||||
"send 失败: {} [timeout={} connect={} body={} request={} src={:?}]",
|
||||
e,
|
||||
e.is_timeout(),
|
||||
e.is_connect(),
|
||||
e.is_body(),
|
||||
e.is_request(),
|
||||
std::error::Error::source(&e)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user