修复: 队列发送失败回填不丢消息+池权重revert+lang解析DRY+注释变量名修正

This commit is contained in:
2026-06-17 03:55:30 +08:00
parent 6b2dd8fb5d
commit a622bdca61
9 changed files with 111 additions and 32 deletions

View File

@@ -336,8 +336,10 @@ impl AnthropicCompatProvider {
// F-260614-05 Phase 2a: 多模态 user 消息 → content blocks 数组text/image
// 含图时把 content + parts 拍平成 blocksText 片 → {type:text}
// Image 片 → {type:image, source:{type:base64, media_type, data}}。
// Anthropic 协议要求 image 必须内嵌 base64不接受 URL 直传)
// url 模式应由 commands 层预拉字节回填 base64provider 不发额外 HTTP
// Anthropic 协议要求 image 必须内嵌 base64不接受 URL 直传)
// 现状前端Phase2b只产 base64 模式图片片url 模式当前不可达
// 未来若加 url 图片输入,必须在 commands 层补 url→base64 预拉
//provider 不发额外 HTTP否则下方兜底会发空 data 致 Anthropic 400。
// 纯文本消息(无图)保持原字符串简写,与现有端点零回归。
if m.has_image() {
let blocks: Vec<serde_json::Value> = m
@@ -351,12 +353,13 @@ impl AnthropicCompatProvider {
crate::provider::ContentPart::Image { url, base64, media_type, alt: _ } => {
let mt = media_type.clone().unwrap_or_else(|| "image/png".into());
let data = base64.clone().unwrap_or_else(|| {
// url 模式无 base64 时 provider 层兜底commands 层应已预拉):
// 发空 data 会让 Anthropic 报错warn 提示但不阻塞。
// 完整性兜底:当前 url 模式不可达(前端 Phase2b 只产 base64 图片片)。
// 若未来接入 url 图片输入而 commands 层未补 url→base64 预拉,
// 此处会发空 data 致 Anthropic 400warn 留痕但不阻塞(避免静默吞数据)。
if url.is_some() {
warn!(
url = ?url,
"Anthropic user 消息含 Image(url) 但未预拉 base64将发空 datacommands 层应回填 base64"
"Anthropic user 消息含 Image(url) 但 base64 缺失,将发空 datacommands 层未补 url→base64 预拉"
);
}
String::new()

View File

@@ -332,7 +332,12 @@ impl OpenAICompatProvider {
format!("data:{};base64,{}", mt, b)
}
(None, Some(u), _) => u,
// 兜底:缺数据时退化为占位,避免发空 url 触发 400
// 完整性兜底:当前 image_base64 构造器强制 media_type:Some
// image_url 构造器提供 url:Some二者分别命中上两个分支
// 此分支仅在 parts 来源被外部直接构造且字段均缺时才可达
//(如 url:None+base64:None 或 base64:Some+media_type:None
// 退化为空串OpenAI 对空 image_url.url 会 400
// 由调用方保证 parts 合法性provider 层不做静默伪造。
_ => String::new(),
};
serde_json::json!({

View File

@@ -101,7 +101,7 @@ pub fn get_provider_secret(id: &str) -> Option<String> {
}
}
/// 消费点用:解析 provider 真实密钥 — keyring 优先,fallback DB.api_key(兼容未迁移老库)
/// 消费点用:解析 provider 真实密钥 — DB 优先,fallback keyring(兼容未迁移老库)
pub fn resolve_provider_secret(record: &AiProviderRecord) -> String {
if !record.api_key.is_empty() {
return record.api_key.clone();