修复: AI 消息卡片友好展示
- update_task 卡片后端补返 title + 前端 title 优先 + i18n 文案对齐(UX-260619-04,防裸 UUID id 辨识度低) - anthropic_compat 合并连续 user 块,修 GLM 1214 协议交替违规(B-260619-03)
This commit is contained in:
@@ -216,6 +216,12 @@ impl AnthropicCompatProvider {
|
||||
}
|
||||
Self::flush_tool_results(&mut messages, &mut pending_tool_results);
|
||||
|
||||
// B-260619-03: 合并相邻 user 块。Anthropic 协议要求 user/assistant 严格交替,连续 user
|
||||
// 触发 GLM 1214。场景:drainQueue 续发(前一轮以 tool_result 结尾 + 新 user)→ flush 把
|
||||
// tool_result 转成 user 后紧跟 push 新 user → 连续两 user。合并成一条 user 含
|
||||
// [tool_result..., text] blocks(Anthropic 允许一条 user 多 blocks),打破恶性循环。
|
||||
Self::merge_consecutive_users(&mut messages);
|
||||
|
||||
let tools = req.tools.map(|defs| {
|
||||
defs.into_iter()
|
||||
.map(|d| AnthropicToolDef {
|
||||
@@ -256,6 +262,39 @@ impl AnthropicCompatProvider {
|
||||
messages.push(serde_json::json!({ "role": "user", "content": blocks }));
|
||||
}
|
||||
|
||||
/// B-260619-03: 合并相邻 user 消息为一条(content 拼成 blocks 数组)。
|
||||
/// Anthropic 协议要求 user/assistant 严格交替,连续 user 触发 1214。
|
||||
/// 触发场景:flush_tool_results 把 tool_result 转 user 后紧跟新 user(drainQueue 续发,
|
||||
/// 前一轮以 tool_result 结尾)。合并成一条 user 含 [tool_result..., text] blocks,合法。
|
||||
/// content 形态:string(简写)或 array(blocks),统一规范化为 array 后拼接。
|
||||
fn merge_consecutive_users(messages: &mut Vec<serde_json::Value>) {
|
||||
let is_user = |m: &serde_json::Value| m.get("role").and_then(|r| r.as_str()) == Some("user");
|
||||
let mut i = 0;
|
||||
while i + 1 < messages.len() {
|
||||
if is_user(&messages[i]) && is_user(&messages[i + 1]) {
|
||||
let next_content = messages[i + 1].get("content").cloned().unwrap_or(serde_json::Value::Null);
|
||||
let cur = &mut messages[i];
|
||||
// 当前 user content 规范化为 array
|
||||
let mut blocks: Vec<serde_json::Value> = match cur.get("content").cloned() {
|
||||
Some(serde_json::Value::String(s)) => vec![serde_json::json!({ "type": "text", "text": s })],
|
||||
Some(serde_json::Value::Array(a)) => a,
|
||||
_ => vec![],
|
||||
};
|
||||
// 并入下一个 user content
|
||||
match next_content {
|
||||
serde_json::Value::String(s) => blocks.push(serde_json::json!({ "type": "text", "text": s })),
|
||||
serde_json::Value::Array(a) => blocks.extend(a),
|
||||
_ => {}
|
||||
}
|
||||
cur["content"] = serde_json::Value::Array(blocks);
|
||||
messages.remove(i + 1);
|
||||
// 不增 i:继续合并 i 与新 i+1(可能多个连续 user)
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 生成 messages 诊断摘要(每条 role + content 形态 + tool 标记),不含敏感数据。
|
||||
/// B-260618-27: 1214 类错误时随 bail 文案直达前端 raw,定位哪条/字段非法。
|
||||
fn summarize_messages(messages: &[serde_json::Value]) -> String {
|
||||
|
||||
@@ -675,7 +675,10 @@ fn register_task_tools(registry: &mut AiToolRegistry, db: &Arc<Database>) {
|
||||
}
|
||||
let repo = df_storage::crud::TaskRepo::new(&db);
|
||||
repo.update_field(id, field, value).await?;
|
||||
Ok(serde_json::json!({ "id": id, "field": field, "updated": true }))
|
||||
// UX-260619-04: 返回 title 供前端卡片友好展示(对齐 advance_task 返 TaskRecord,
|
||||
// 防裸 UUID id 辨识度低)。update_field 返 bool 不含 record,单独 get_by_id 取 title。
|
||||
let title = repo.get_by_id(id).await?.map(|t| t.title).unwrap_or_default();
|
||||
Ok(serde_json::json!({ "id": id, "title": title, "field": field, "updated": true }))
|
||||
})
|
||||
})},
|
||||
);
|
||||
|
||||
@@ -369,8 +369,9 @@ function formatAdvanceTask(r: ToolResult): string {
|
||||
|
||||
/** update_task 共享格式化: updated!==false 显字段更新,否则通用 updated */
|
||||
function formatUpdateTask(r: ToolResult): string {
|
||||
// UX-260619-04: title 优先(后端补返 title),无则回退 id(防裸 UUID),对齐 advance_task。
|
||||
return r.updated !== false
|
||||
? t('aiTool.updatedTaskField', { id: r.id || '', field: r.field || '' })
|
||||
? t('aiTool.updatedTaskField', { title: r.title || r.id || '', field: r.field || '' })
|
||||
: t('aiTool.updated')
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ export default {
|
||||
workflowTriggeredShort: 'Execution {exec}',
|
||||
|
||||
// UX-260616-04: formatToolResult covers all tools with human-readable summaries (missing keys)
|
||||
updatedTaskField: 'Task {id} updated field {field}',
|
||||
updatedTaskField: 'Task「{title}」 updated field {field}',
|
||||
advancedTask: 'Task「{title}」 advanced to {status}',
|
||||
// UX-260618-09: advance_task return-round accumulation (shown only when >0); UX-260618-14: {title} = task name (TaskRecord), fallback id
|
||||
advancedTaskWithRounds: 'Task「{title}」 advanced to {status} (returned {n} times)',
|
||||
|
||||
@@ -64,7 +64,7 @@ export default {
|
||||
workflowTriggeredShort: '执行实例 {exec}',
|
||||
|
||||
// UX-260616-04:formatToolResult 覆盖全部工具产出人类可读摘要(补缺失 key)
|
||||
updatedTaskField: '任务 {id} 已更新字段 {field}',
|
||||
updatedTaskField: '任务「{title}」已更新字段 {field}',
|
||||
advancedTask: '任务「{title}」已推进至 {status}',
|
||||
// UX-260618-09:advance_task 退回累加轮数(>0 才显);UX-260618-14:{title} 显任务名(后端返 TaskRecord),回退 id
|
||||
advancedTaskWithRounds: '任务「{title}」已推进至 {status}(已退回 {n} 次)',
|
||||
|
||||
Reference in New Issue
Block a user