修复: DeepSeek reasoning_content 全链路透传(跨6 crate补字段+映射+agentic loop累积回传)

This commit is contained in:
2026-06-17 18:08:04 +08:00
parent 1cd7652a34
commit 74003bc04f
11 changed files with 251 additions and 20 deletions

View File

@@ -101,6 +101,8 @@ pub(crate) enum StreamResult {
text: String,
tool_calls: HashMap<u32, ToolCallDraft>,
usage: df_ai::provider::TokenUsage,
/// DeepSeek thinking 模式推理内容(需回传到下一轮请求)
reasoning_content: Option<String>,
},
/// 流中途断MidStream chunk Err / idle timeout / provider stream-error / 有 partial_text
/// 但流尽未收到 finished。已 emit 过 AiTextDelta前端 currentText 已累积),
@@ -110,6 +112,8 @@ pub(crate) enum StreamResult {
text: String,
tool_calls: HashMap<u32, ToolCallDraft>,
usage: df_ai::provider::TokenUsage,
/// DeepSeek thinking 模式推理内容(部分累积)
reasoning_content: Option<String>,
},
/// Init 失败provider.stream() Err建连/鉴权/HTTP non-2xx。已 emit AiError。
/// `retryable`: 据状态码分类(retry::is_status_retryable 镜像)true=5xx/429/timeout/connect
@@ -154,6 +158,8 @@ pub(crate) async fn stream_llm(
let mut finished_received = false;
let mut stopped = false;
let mut final_usage: Option<df_ai::provider::TokenUsage> = None;
// BUG-260617-12: DeepSeek thinking 模式推理内容累积(多轮需回传)
let mut reasoning_content_acc: Option<String> = None;
// B-260615-15:heartbeat interval 提至 loop 外复用,避免每轮重建计时器
// (每轮重建会丢已积累的节拍,且 interval 首次 tick 立即返回的特性会被误用)。
@@ -208,6 +214,7 @@ pub(crate) async fn stream_llm(
text: full_text,
tool_calls: tool_calls_acc,
usage: final_usage.unwrap_or_default(),
reasoning_content: reasoning_content_acc,
};
}
Ok(None) => break, // 流正常结束
@@ -231,6 +238,10 @@ pub(crate) async fn stream_llm(
if let Some(u) = &chunk.usage {
final_usage = Some(u.clone());
}
// BUG-260617-12: DeepSeek thinking 模式推理内容累积
if let Some(ref rc) = chunk.reasoning_content {
reasoning_content_acc.get_or_insert_with(String::new).push_str(rc);
}
// provider 流式错误事件Anthropic SSE `type=="error"` 等):
// UX-2025-04 / CR-30-2 / 决策 a1: MidStream 类失败——已有文本则保文(Partial),
// 不重试。空文本无文可保,emit AiError + InitFailed(保守 retryable=true,
@@ -260,6 +271,7 @@ pub(crate) async fn stream_llm(
text: full_text,
tool_calls: tool_calls_acc,
usage: final_usage.unwrap_or_default(),
reasoning_content: reasoning_content_acc,
};
}
if chunk.finished {
@@ -301,6 +313,7 @@ pub(crate) async fn stream_llm(
text: full_text,
tool_calls: tool_calls_acc,
usage: final_usage.unwrap_or_default(),
reasoning_content: reasoning_content_acc,
};
}
}
@@ -333,6 +346,7 @@ pub(crate) async fn stream_llm(
text: full_text,
tool_calls: tool_calls_acc,
usage: final_usage.unwrap_or_default(),
reasoning_content: reasoning_content_acc,
};
}
@@ -358,6 +372,7 @@ pub(crate) async fn stream_llm(
text: full_text,
tool_calls: tool_calls_acc,
usage: final_usage.unwrap_or_default(),
reasoning_content: reasoning_content_acc,
};
}
@@ -366,6 +381,7 @@ pub(crate) async fn stream_llm(
text: full_text,
tool_calls: tool_calls_acc,
usage: final_usage.unwrap_or_default(),
reasoning_content: reasoning_content_acc,
}
}
Err(e) => {