返回 DeepSeek-Reasonix
reasoning_snapshots.go
根目录 / internal / provider / responses / reasoning_snapshots.go
1 package responses
2
3 import (
4 "context"
5 "encoding/json"
6 "reasonix/internal/provider"
7 )
8
9 type responseReasoningSnapshots struct{ items []json.RawMessage }
10
11 func (s *responseReasoningSnapshots) capture(ev sseEvent) {
12 var candidates []sseItem
13 if ev.Type == "response.output_item.done" && ev.Item != nil {
14 candidates = append(candidates, *ev.Item)
15 }
16 if ev.Type == "response.completed" && ev.Response != nil {
17 candidates = append(candidates, ev.Response.Output...)
18 }
19 for _, item := range candidates {
20 if provider.IsReplayableResponsesReasoning(item.Raw) {
21 s.items = provider.UpsertResponsesItem(s.items, item.Raw)
22 }
23 }
24 }
25 func (s *responseReasoningSnapshots) emit(ctx context.Context, out chan<- provider.Chunk) bool {
26 for _, item := range s.items {
27 if !sendChunk(ctx, out, provider.Chunk{Type: provider.ChunkResponsesItem, ResponsesItem: item}) {
28 return false
29 }
30 }
31 return true
32 }
33 func (s *responseReasoningSnapshots) metadata() (string, string) {
34 var item sseItem
35 _ = json.Unmarshal(s.items[len(s.items)-1], &item)
36 return item.ID, item.Status
37 }
38 func appendReasoningItems(input []map[string]any, items []json.RawMessage) ([]map[string]any, bool) {
39 found := false
40 for _, raw := range items {
41 if provider.IsReplayableResponsesReasoning(raw) {
42 var item map[string]any
43 if json.Unmarshal(raw, &item) == nil {
44 input = append(input, item)
45 found = true
46 }
47 }
48 }
49 return input, found
50 }
51
52 func terminalResponseID(event sseEvent) string {
53 if event.Type == "response.completed" && event.Response != nil {
54 return event.Response.ID
55 }
56 return ""
57 }
58 func emitTerminalResponseUsage(ctx context.Context, out chan<- provider.Chunk, event sseEvent) bool {
59 if event.Response != nil {
60
61 usage := usageFromResponse(event.Response)
62 provider.ApplyRequestAttemptCount(ctx, usage)
63 if event.Type == "response.incomplete" {
64 switch event.Response.IncompleteDetails.Reason {
65 case "max_output_tokens":
66 usage.FinishReason = "length"
67 case "content_filter":
68 usage.FinishReason = "content_filter"
69 default:
70 usage.FinishReason = "incomplete"
71 }
72 } else if event.Type == "response.completed" && usage.FinishReason == "" {
73 // A completed response finished normally (stop). Preserve any
74 // vendor-specific reason already set by usageFromResponse.
75 usage.FinishReason = "stop"
76 }
77 // Preserve a terminal finish reason even when the provider reports zero
78 // usage, so reasoning-only completion is not mistaken for an empty reply.
79 if usage.TotalTokens > 0 || usage.FinishReason != "" {
80
81 if !sendChunk(ctx, out, provider.Chunk{Type: provider.ChunkUsage, Usage: usage}) {
82 return false
83 }
84 }
85 }
86
87 return true
88 }
89
89 lines GO