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