修复: 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);
|
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| {
|
let tools = req.tools.map(|defs| {
|
||||||
defs.into_iter()
|
defs.into_iter()
|
||||||
.map(|d| AnthropicToolDef {
|
.map(|d| AnthropicToolDef {
|
||||||
@@ -256,6 +262,39 @@ impl AnthropicCompatProvider {
|
|||||||
messages.push(serde_json::json!({ "role": "user", "content": blocks }));
|
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 标记),不含敏感数据。
|
/// 生成 messages 诊断摘要(每条 role + content 形态 + tool 标记),不含敏感数据。
|
||||||
/// B-260618-27: 1214 类错误时随 bail 文案直达前端 raw,定位哪条/字段非法。
|
/// B-260618-27: 1214 类错误时随 bail 文案直达前端 raw,定位哪条/字段非法。
|
||||||
fn summarize_messages(messages: &[serde_json::Value]) -> String {
|
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);
|
let repo = df_storage::crud::TaskRepo::new(&db);
|
||||||
repo.update_field(id, field, value).await?;
|
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 */
|
/** update_task 共享格式化: updated!==false 显字段更新,否则通用 updated */
|
||||||
function formatUpdateTask(r: ToolResult): string {
|
function formatUpdateTask(r: ToolResult): string {
|
||||||
|
// UX-260619-04: title 优先(后端补返 title),无则回退 id(防裸 UUID),对齐 advance_task。
|
||||||
return r.updated !== false
|
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')
|
: t('aiTool.updated')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export default {
|
|||||||
workflowTriggeredShort: 'Execution {exec}',
|
workflowTriggeredShort: 'Execution {exec}',
|
||||||
|
|
||||||
// UX-260616-04: formatToolResult covers all tools with human-readable summaries (missing keys)
|
// 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}',
|
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
|
// 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)',
|
advancedTaskWithRounds: 'Task「{title}」 advanced to {status} (returned {n} times)',
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export default {
|
|||||||
workflowTriggeredShort: '执行实例 {exec}',
|
workflowTriggeredShort: '执行实例 {exec}',
|
||||||
|
|
||||||
// UX-260616-04:formatToolResult 覆盖全部工具产出人类可读摘要(补缺失 key)
|
// UX-260616-04:formatToolResult 覆盖全部工具产出人类可读摘要(补缺失 key)
|
||||||
updatedTaskField: '任务 {id} 已更新字段 {field}',
|
updatedTaskField: '任务「{title}」已更新字段 {field}',
|
||||||
advancedTask: '任务「{title}」已推进至 {status}',
|
advancedTask: '任务「{title}」已推进至 {status}',
|
||||||
// UX-260618-09:advance_task 退回累加轮数(>0 才显);UX-260618-14:{title} 显任务名(后端返 TaskRecord),回退 id
|
// UX-260618-09:advance_task 退回累加轮数(>0 才显);UX-260618-14:{title} 显任务名(后端返 TaskRecord),回退 id
|
||||||
advancedTaskWithRounds: '任务「{title}」已推进至 {status}(已退回 {n} 次)',
|
advancedTaskWithRounds: '任务「{title}」已推进至 {status}(已退回 {n} 次)',
|
||||||
|
|||||||
Reference in New Issue
Block a user