修复: PowerShell 路径转义 + shell 失败提示 + 标题合并同 role + L0 防抖
- run_command 路径反斜杠传递修正 - 命令失败追加 PowerShell 适配提示引导 LLM 改正 - 标题生成前合并连续同 role 消息避免 Anthropic 1214 - V33 迁移追加 workflow_executions.updated_at 列 - ai-client-ready 3 秒防抖避免重复握手
This commit is contained in:
@@ -76,6 +76,12 @@ pub(crate) async fn ensure_conversation_title(
|
||||
return;
|
||||
}
|
||||
|
||||
// 任务3: 合并相邻同 role 消息(防 Anthropic 协议 1214 错误)。
|
||||
// session.messages 历史可能存在连续同 role(如 user 失败重提、助手多轮中断后续接、tool_result 被过滤后
|
||||
// user 连续),Anthropic API 严格校验「messages 必须 user/assistant 交替」,连续同 role 直接拒 1214。
|
||||
// 在传给 LLM 前合并连续同 role(拼接 content),不影响其他逻辑(本函数 summary 派生数据)。
|
||||
let summary_msgs = merge_consecutive_roles(summary_msgs);
|
||||
|
||||
// B-260617-17 修复:进入即 extract_title 兜底落库 + emit,保证侧栏即时有非"新对话"标题。
|
||||
// 原实现仅在 LLM 返回 None(失败)或 provider 构建失败时才落 extract——但 LLM 卡住
|
||||
// (generate_title_via_llm 的 llm_concurrency 信号量 acquire 阻塞 / 网络挂起 / 后台
|
||||
@@ -208,3 +214,36 @@ pub(crate) fn extract_title(messages: &[ChatMessage]) -> Option<String> {
|
||||
if chars.next().is_some() { format!("{}...", t) } else { t }
|
||||
})
|
||||
}
|
||||
|
||||
/// 合并相邻同 role 消息,拼接 content(防 Anthropic 1214 连续同 role 错误)。
|
||||
/// role 比较: System/User/Assistant/Tool 中,User 和 Assistant 交替校验最严,
|
||||
/// 不同 role 不合并(保留边界),仅合并连续同 role。
|
||||
fn merge_consecutive_roles(msgs: Vec<ChatMessage>) -> Vec<ChatMessage> {
|
||||
if msgs.is_empty() {
|
||||
return msgs;
|
||||
}
|
||||
let mut out: Vec<ChatMessage> = Vec::with_capacity(msgs.len());
|
||||
for m in msgs {
|
||||
if let Some(last) = out.last_mut() {
|
||||
// role 比较(MessageRole 未派生 PartialEq,用 matches!按变体逐个比)。
|
||||
let same_role = match (&last.role, &m.role) {
|
||||
(MessageRole::System, MessageRole::System)
|
||||
| (MessageRole::User, MessageRole::User)
|
||||
| (MessageRole::Assistant, MessageRole::Assistant)
|
||||
| (MessageRole::Tool, MessageRole::Tool) => true,
|
||||
_ => false,
|
||||
};
|
||||
if same_role {
|
||||
// 连续同 role: 拼接 content(中间补换行,避免语义粘连),保留首条元数据(id/timestamp)。
|
||||
// 不拼接 reasoning_content(标题摘要场景该字段无用,且不同 turn reasoning 串一起无意义)。
|
||||
if !m.content.is_empty() {
|
||||
last.content.push('\n');
|
||||
last.content.push_str(&m.content);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
out.push(m);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user