返回 DeepSeek-Reasonix
types_ext.go
根目录 / sdk / go / types_ext.go
1 package extension
2
3 import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "strings"
8 )
9
10 // This file holds the handwritten half of the SDK's type layer: behavior the
11 // generator cannot emit (validators, error constructors, enum helpers). The
12 // wire DTOs, enums, method names, frozen limits, and the frozen error table
13 // live in types_generated.go, mirrored from the host's
14 // internal/extension/protocol package by cmd/extension-protocol-gen.
15 //
16 // Stability contract (mirrored from the host): within major version 1 only
17 // optional fields, new enum values, and new methods may be added. Existing
18 // required fields, method names, directions, limits, error reasons, and
19 // semantics never change.
20
21 // ---------------------------------------------------------------------------
22 // Enum helpers
23 // ---------------------------------------------------------------------------
24
25 // InterceptEvents returns the 17 frozen hook point names, sorted.
26 func InterceptEvents() []string {
27 out := []string{
28 string(EventSessionStart), string(EventSessionEnd), string(EventSessionLoad),
29 string(EventSessionSave), string(EventSessionRotate), string(EventInputReceive),
30 string(EventAgentBeforeStart), string(EventSystemPromptBuild), string(EventContextPrepare),
31 string(EventProviderRequest), string(EventProviderResponse), string(EventToolBefore),
32 string(EventToolAfter), string(EventPermissionDecision), string(EventCompactionPrepare),
33 string(EventCompactionComplete), string(EventFrontendEvent),
34 }
35 sortStrings(out)
36 return out
37 }
38
39 func validInterceptEvent(event InterceptEvent) bool {
40 switch event {
41 case EventSessionStart, EventSessionEnd, EventSessionLoad, EventSessionSave,
42 EventSessionRotate, EventInputReceive, EventAgentBeforeStart,
43 EventSystemPromptBuild, EventContextPrepare, EventProviderRequest,
44 EventProviderResponse, EventToolBefore, EventToolAfter,
45 EventPermissionDecision, EventCompactionPrepare, EventCompactionComplete,
46 EventFrontendEvent:
47 return true
48 }
49 return false
50 }
51
52 func validInterceptDecision(decision InterceptDecision) bool {
53 switch decision {
54 case DecisionContinue, DecisionBlock, DecisionReplace, DecisionAllow, DecisionDeny:
55 return true
56 }
57 return false
58 }
59
60 func validUIFieldKind(kind UIFieldKind) bool {
61 switch kind {
62 case UIFieldConfirm, UIFieldInput, UIFieldSelect, UIFieldMultiselect:
63 return true
64 }
65 return false
66 }
67
68 func validUISeverity(severity UISeverity) bool {
69 switch severity {
70 case "", UISeverityInfo, UISeverityWarn, UISeverityError:
71 return true
72 }
73 return false
74 }
75
76 // ---------------------------------------------------------------------------
77 // Wire DTO validators
78 // ---------------------------------------------------------------------------
79
80 // Validate enforces the deterministic wire shape.
81 func (request ProviderRequest) Validate() error {
82 if request.Messages == nil || request.Tools == nil {
83 return validationError("messages and tools must be arrays")
84 }
85 if request.MaxTokens < 0 {
86 return validationError("maxTokens must be non-negative")
87 }
88 for _, tool := range request.Tools {
89 parameters := bytes.TrimSpace(tool.Parameters)
90 if len(parameters) == 0 || parameters[0] != '{' || !json.Valid(parameters) {
91 return validationError("tool parameters must be a JSON object")
92 }
93 }
94 return nil
95 }
96
97 // Validate enforces chunk invariants the tags cannot express.
98 func (chunk ProviderChunk) Validate() error {
99 if chunk.ArgChars < 0 {
100 return validationError("argChars must be non-negative")
101 }
102 if chunk.Type == ChunkError && chunk.Error == nil {
103 return validationError("error chunks require error")
104 }
105 if chunk.Type != ChunkError && chunk.Error != nil {
106 return validationError("non-error chunks forbid error")
107 }
108 if chunk.Type == ChunkUsage && chunk.Usage == nil {
109 return validationError("usage chunks require usage")
110 }
111 return nil
112 }
113
114 // Validate enforces required identifiers plus the request invariants.
115 func (p StreamOpenParams) Validate() error {
116 if strings.TrimSpace(p.StreamID) == "" || strings.TrimSpace(p.ProviderRef) == "" {
117 return validationError("streamId and providerRef are required")
118 }
119 return p.Request.Validate()
120 }
121
122 // Validate enforces stream ordering preconditions and chunk invariants.
123 func (p StreamChunkParams) Validate() error {
124 if strings.TrimSpace(p.StreamID) == "" {
125 return validationError("streamId is required")
126 }
127 if p.Seq < 1 {
128 return validationError("seq must be >= 1")
129 }
130 return p.Chunk.Validate()
131 }
132
133 // Validate checks the data against the frozen error table.
134 func (d ProtocolErrorData) Validate() error {
135 spec, ok := frozenErrorSpecs[d.Reason]
136 if !ok {
137 return fmt.Errorf("unknown extension error reason %q", d.Reason)
138 }
139 if d.Retryable != spec.Retryable {
140 return fmt.Errorf("retryable must match the frozen error table for %q", d.Reason)
141 }
142 return nil
143 }
144
145 // ---------------------------------------------------------------------------
146 // ProtocolError
147 // ---------------------------------------------------------------------------
148
149 // ProtocolError is the extension protocol's structured error. Handlers may
150 // return one to choose the wire reason; the SDK answers with the frozen
151 // JSON-RPC code and structured data. Message should stay generic: it crosses
152 // the wire verbatim.
153 type ProtocolError struct {
154 Reason ErrorReason
155 Message string
156 }
157
158 func (e *ProtocolError) Error() string {
159 if e == nil {
160 return ""
161 }
162 return e.Message
163 }
164
165 // NewProtocolError builds the frozen error for a reason.
166 func NewProtocolError(reason ErrorReason) (*ProtocolError, error) {
167 spec, ok := frozenErrorSpecs[reason]
168 if !ok {
169 return nil, fmt.Errorf("extension: unknown error reason %q", reason)
170 }
171 return &ProtocolError{Reason: reason, Message: spec.Message}, nil
172 }
173
174 // MustProtocolError is NewProtocolError for reasons known to be frozen.
175 func MustProtocolError(reason ErrorReason) *ProtocolError {
176 errValue, err := NewProtocolError(reason)
177 if err != nil {
178 panic(err)
179 }
180 return errValue
181 }
182
183 // ---------------------------------------------------------------------------
184 // internal helpers shared with the validator
185 // ---------------------------------------------------------------------------
186
187 type validationFailure struct{ message string }
188
189 func (e *validationFailure) Error() string { return e.message }
190
191 func validationError(message string) error { return &validationFailure{message: message} }
192
193 func sortStrings(values []string) {
194 for i := 1; i < len(values); i++ {
195 for j := i; j > 0 && values[j] < values[j-1]; j-- {
196 values[j], values[j-1] = values[j-1], values[j]
197 }
198 }
199 }
200
200 lines GO