Version: 0.9.53.dev.260429

后端:
1. 流式思考链路从 raw reasoning_content 切到 `thinking_summary` 摘要协议,补齐摘要 prompt、digestor 与 Lite 压缩链路,plan / execute / fallback 统一改为“只出摘要、不透原始推理”,正文开始后自动关停摘要流。
2. thinking_summary 打通 timeline / SSE / outbox 持久化闭环,只落 detail_summary 与必要 metadata,并补强 seq 自检、冲突幂等识别与补 seq 回填,提升重放恢复稳定性。
3. 会话历史口径继续收紧,assistant 正文与时间线不再回写 raw reasoning_content,仅保留正文与思考耗时,避免刷新恢复时再次暴露内部推理文本。

前端:
4. 助手页开始接入 thinking_summary 实时流与历史恢复,补齐短摘要状态、长摘要折叠区、正文开流后自动收口,并增加调试入口用于协议联调与验收。
5. 当前前端助手页仍是残次过渡态,本版先以 thinking_summary 协议接通和基础渲染为主,样式、交互与细节体验暂未收平,下一版集中修复。

仓库:
6. 补充 thinking_summary 对接说明,明确 SSE 协议、timeline 恢复口径与 short/detail summary 的使用边界。
This commit is contained in:
Losita
2026-04-29 01:00:38 +08:00
parent d89e2830a9
commit f81f137791
21 changed files with 8566 additions and 229 deletions

View File

@@ -6,6 +6,7 @@ import (
"strings"
"time"
infrallm "github.com/LoveLosita/smartflow/backend/infra/llm"
newagentprompt "github.com/LoveLosita/smartflow/backend/newAgent/prompt"
newagentstream "github.com/LoveLosita/smartflow/backend/newAgent/stream"
"github.com/cloudwego/eino-ext/components/model/ark"
@@ -25,6 +26,8 @@ func (s *AgentService) streamChatFallback(
chatHistory []*schema.Message,
outChan chan<- string,
reasoningStartAt *time.Time,
userID int,
chatID string,
) (string, string, int, *schema.TokenUsage, error) {
messages := make([]*schema.Message, 0, len(chatHistory)+2)
messages = append(messages, schema.SystemMessage(newagentprompt.SystemPrompt))
@@ -46,6 +49,24 @@ func (s *AgentService) streamChatFallback(
requestID := "chatcmpl-" + uuid.NewString()
created := time.Now().Unix()
firstChunk := true
chunkEmitter := newagentstream.NewChunkEmitter(newagentstream.NewSSEPayloadEmitter(outChan), requestID, modelName, created)
chunkEmitter.SetReasoningSummaryFunc(s.makeReasoningSummaryFunc(infrallm.WrapArkClient(s.AIHub.Lite)))
chunkEmitter.SetExtraEventHook(func(extra *newagentstream.OpenAIChunkExtra) {
s.persistNewAgentTimelineExtraEvent(context.Background(), userID, chatID, extra)
})
reasoningDigestor, digestorErr := chunkEmitter.NewReasoningDigestor(ctx, "fallback.speak", "fallback")
if digestorErr != nil {
return "", "", 0, nil, digestorErr
}
digestorClosed := false
closeDigestor := func() {
if reasoningDigestor == nil || digestorClosed {
return
}
digestorClosed = true
_ = reasoningDigestor.Close(ctx)
}
defer closeDigestor()
var localReasoningStartAt *time.Time
if reasoningStartAt != nil && !reasoningStartAt.IsZero() {
@@ -61,7 +82,6 @@ func (s *AgentService) streamChatFallback(
defer reader.Close()
var fullText strings.Builder
var reasoningText strings.Builder
var tokenUsage *schema.TokenUsage
for {
chunk, recvErr := reader.Recv()
@@ -85,26 +105,31 @@ func (s *AgentService) streamChatFallback(
now := time.Now()
reasoningEndAt = &now
}
fullText.WriteString(chunk.Content)
reasoningText.WriteString(chunk.ReasoningContent)
}
payload, payloadErr := newagentstream.ToOpenAIStream(chunk, requestID, modelName, created, firstChunk)
if payloadErr != nil {
return "", "", 0, nil, payloadErr
}
if payload != "" {
outChan <- payload
firstChunk = false
// 1. fallback 链路同样不能透传 raw reasoning_content
// 2. 只把 reasoning 喂给摘要器,正文出现时立即关门丢弃后续摘要。
if strings.TrimSpace(chunk.ReasoningContent) != "" && reasoningDigestor != nil {
reasoningDigestor.Append(chunk.ReasoningContent)
}
if chunk.Content != "" {
if reasoningDigestor != nil {
reasoningDigestor.MarkContentStarted()
}
if emitErr := chunkEmitter.EmitAssistantText("fallback.speak", "fallback", chunk.Content, firstChunk); emitErr != nil {
return "", "", 0, nil, emitErr
}
fullText.WriteString(chunk.Content)
firstChunk = false
}
}
}
closeDigestor()
finishChunk, finishErr := newagentstream.ToOpenAIFinishStream(requestID, modelName, created)
if finishErr != nil {
if finishErr := chunkEmitter.EmitFinish("fallback.speak", "fallback"); finishErr != nil {
return "", "", 0, nil, finishErr
}
outChan <- finishChunk
outChan <- "[DONE]"
if doneErr := chunkEmitter.EmitDone(); doneErr != nil {
return "", "", 0, nil, doneErr
}
reasoningDurationSeconds := 0
if localReasoningStartAt != nil {
@@ -117,5 +142,5 @@ func (s *AgentService) streamChatFallback(
}
}
return fullText.String(), reasoningText.String(), reasoningDurationSeconds, tokenUsage, nil
return fullText.String(), "", reasoningDurationSeconds, tokenUsage, nil
}