返回 DeepSeek-Reasonix
projection.go
根目录 / internal / provider / projection.go
1 package provider
2
3 import "slices"
4
5 // Two copies of a transcript are derived from the stored one: the bytes a
6 // provider receives, and the projection compaction writes back. They differ in
7 // exactly one field, and that difference is load-bearing.
8
9 // ModelMessages removes durable display-only records before a request is
10 // handed to any provider. Healthy sessions without such records keep their
11 // original backing slice, preserving the allocation and prompt-cache fast path.
12 func ModelMessages(msgs []Message) []Message { return projectMessages(msgs, false, false) }
13
14 // ProjectionMessages is ModelMessages for a stored projection, except that
15 // ToolExecution and Origin survive: a projection is also the next compaction's
16 // input, and those records classify tool failures and host-authored protocol
17 // messages. Stripping belongs at the provider boundary, which every request
18 // path already crosses.
19 func ProjectionMessages(msgs []Message) []Message { return projectMessages(msgs, true, true) }
20
21 func messagesNeedProjection(msgs []Message, keepExecution, keepOrigin bool) bool {
22 for _, m := range msgs {
23 if m.InterruptedTurn != nil || slices.ContainsFunc(m.ToolCalls, func(c ToolCall) bool { return c.Recovery != nil }) || m.ReadPause != nil || m.ReadCompletion != nil || len(m.ToolDiagnostic) > 0 {
24 return true
25 }
26 if slices.ContainsFunc(m.ServerSearch, func(s ServerSearchCall) bool { return s.SourcesStatus != "" }) || len(m.ProtocolRecovery) > 0 || (!keepExecution && slices.ContainsFunc(m.ToolCalls, func(c ToolCall) bool { return len(c.WriteIntents) > 0 })) || m.LocalOnly || (!keepOrigin && m.Origin != "") || m.RawContent != "" || m.ProviderContent != "" || m.DecisionReceipt != nil || len(m.DecisionReceipts) > 0 || m.VisionSummary != nil || m.MCPApp != nil || len(m.ReadResult) > 0 || (!keepExecution && m.PresentedFiles != nil) || ((m.ToolExecution != nil || m.ToolRunState != "") && !keepExecution) {
27 return true
28 }
29 }
30 return false
31 }
32
33 func projectMessages(msgs []Message, keepExecution, keepOrigin bool) []Message {
34 if !messagesNeedProjection(msgs, keepExecution, keepOrigin) {
35 return msgs
36 }
37 out := make([]Message, 0, len(msgs))
38 for _, candidate := range msgs {
39 if candidate.LocalOnly {
40 continue
41 }
42 if candidate.ProviderContent != "" {
43 candidate.Content = candidate.ProviderContent
44 candidate.ProviderContent = ""
45 }
46 candidate.RawContent = ""
47 candidate.ProtocolRecovery = nil
48 // Read delivery envelopes are host evidence; they must never change
49 // provider bytes.
50 candidate.ReadResult = nil
51 candidate.ReadPause = nil
52 candidate.InterruptedTurn = nil
53 candidate.ReadCompletion = nil
54 candidate.ToolDiagnostic = nil
55 if !keepExecution && slices.ContainsFunc(candidate.ServerSearch, func(s ServerSearchCall) bool { return s.SourcesStatus != "" }) {
56 candidate.ServerSearch = append([]ServerSearchCall(nil), candidate.ServerSearch...)
57 for i := range candidate.ServerSearch {
58 candidate.ServerSearch[i].SourcesStatus = ""
59 }
60 }
61 if !keepOrigin {
62 candidate.Origin = ""
63 }
64 candidate.DecisionReceipt = nil
65 candidate.DecisionReceipts = nil
66 candidate.VisionSummary = nil
67 // Apps presentation stays local; it must never change provider bytes.
68 candidate.MCPApp = nil
69 if !keepExecution {
70 // Local shell metadata must never enter provider request bytes.
71 candidate.ToolExecution = nil
72 candidate.ToolRunState = ""
73 candidate.PresentedFiles = nil
74 if slices.ContainsFunc(candidate.ToolCalls, func(c ToolCall) bool { return len(c.WriteIntents) > 0 || c.Recovery != nil }) {
75 candidate.ToolCalls = append([]ToolCall(nil), candidate.ToolCalls...)
76 for i := range candidate.ToolCalls {
77 candidate.ToolCalls[i].WriteIntents = nil
78 candidate.ToolCalls[i].Recovery = nil
79 }
80 }
81 }
82 out = append(out, candidate)
83 }
84 return out
85 }
86
86 lines GO