@@ -23,74 +23,317 @@ const ErrorTypeAgentCallbackError = "agent_callback_error"
2323// Returns (customResponse, error).
2424// - customResponse: if not nil, this response will be returned to user and agent execution will be skipped.
2525// - error: if not nil, agent execution will be stopped with this error.
26- type BeforeAgentCallback func (ctx context.Context , invocation * Invocation ) (* model.Response , error )
26+ // Deprecated: Use BeforeAgentCallbackStructured instead for better type safety and context passing.
27+ type BeforeAgentCallback = func (ctx context.Context , invocation * Invocation ) (* model.Response , error )
2728
2829// AfterAgentCallback is called after the agent runs.
2930// Returns (customResponse, error).
3031// - customResponse: if not nil, this response will be used instead of the actual agent response.
3132// - error: if not nil, this error will be returned.
32- type AfterAgentCallback func (ctx context.Context , invocation * Invocation , runErr error ) (* model.Response , error )
33+ // Deprecated: Use AfterAgentCallbackStructured instead for better type safety and context passing.
34+ type AfterAgentCallback = func (ctx context.Context , invocation * Invocation , runErr error ) (* model.Response , error )
35+
36+ // BeforeAgentArgs contains all parameters for before agent callback.
37+ type BeforeAgentArgs struct {
38+ // Invocation is the invocation context.
39+ Invocation * Invocation
40+ }
41+
42+ // BeforeAgentResult contains the return value for before agent callback.
43+ type BeforeAgentResult struct {
44+ // Context if not nil, will be used by the framework for subsequent operations.
45+ Context context.Context
46+ // CustomResponse if not nil, will skip agent execution and return this response.
47+ CustomResponse * model.Response
48+ }
49+
50+ // BeforeAgentCallbackStructured is called before the agent runs.
51+ // Returns (result, error).
52+ // - result: contains optional custom response and context for subsequent operations.
53+ // - CustomResponse: if not nil, this response will be returned to user and agent execution will be skipped.
54+ // - Context: if not nil, will be used by the framework for subsequent operations.
55+ //
56+ // - error: if not nil, agent execution will be stopped with this error.
57+ type BeforeAgentCallbackStructured = func (
58+ ctx context.Context ,
59+ args * BeforeAgentArgs ,
60+ ) (* BeforeAgentResult , error )
61+
62+ // AfterAgentArgs contains all parameters for after agent callback.
63+ type AfterAgentArgs struct {
64+ // Invocation is the invocation context.
65+ Invocation * Invocation
66+ // Error is the error occurred during agent execution (may be nil).
67+ Error error
68+ }
69+
70+ // AfterAgentResult contains the return value for after agent callback.
71+ type AfterAgentResult struct {
72+ // Context if not nil, will be used by the framework for subsequent operations.
73+ Context context.Context
74+ // CustomResponse if not nil, will replace the original response.
75+ CustomResponse * model.Response
76+ }
77+
78+ // AfterAgentCallbackStructured is called after the agent runs.
79+ // Returns (result, error).
80+ // - result: contains optional custom response and context for subsequent operations.
81+ // - CustomResponse: if not nil, this response will be used instead of the actual agent response.
82+ // - Context: if not nil, will be used by the framework for subsequent operations.
83+ //
84+ // - error: if not nil, this error will be returned.
85+ type AfterAgentCallbackStructured = func (
86+ ctx context.Context ,
87+ args * AfterAgentArgs ,
88+ ) (* AfterAgentResult , error )
3389
3490// Callbacks holds callbacks for agent operations.
91+ // Internally stores the new structured callback types.
3592type Callbacks struct {
36- // BeforeAgent is a list of callbacks that are called before the agent runs.
37- BeforeAgent []BeforeAgentCallback
38- // AfterAgent is a list of callbacks that are called after the agent runs.
39- AfterAgent []AfterAgentCallback
93+ // BeforeAgent is a list of callbacks called before the agent runs.
94+ BeforeAgent []BeforeAgentCallbackStructured
95+ // AfterAgent is a list of callbacks called after the agent runs.
96+ AfterAgent []AfterAgentCallbackStructured
97+ // continueOnError controls whether to continue executing callbacks when an error occurs.
98+ // Default: false (stop on first error)
99+ continueOnError bool
100+ // continueOnResponse controls whether to continue executing callbacks when a CustomResponse is returned.
101+ // Default: false (stop on first CustomResponse)
102+ continueOnResponse bool
103+ }
104+
105+ // CallbacksOption configures Callbacks behavior.
106+ type CallbacksOption func (* Callbacks )
107+
108+ // WithContinueOnError sets whether to continue executing callbacks when an error occurs.
109+ func WithContinueOnError (continueOnError bool ) CallbacksOption {
110+ return func (c * Callbacks ) {
111+ c .continueOnError = continueOnError
112+ }
113+ }
114+
115+ // WithContinueOnResponse sets whether to continue executing callbacks when a CustomResponse is returned.
116+ func WithContinueOnResponse (continueOnResponse bool ) CallbacksOption {
117+ return func (c * Callbacks ) {
118+ c .continueOnResponse = continueOnResponse
119+ }
40120}
41121
42122// NewCallbacks creates a new Callbacks instance for agent.
43- func NewCallbacks () * Callbacks {
44- return & Callbacks {}
123+ func NewCallbacks (opts ... CallbacksOption ) * Callbacks {
124+ c := & Callbacks {}
125+ for _ , opt := range opts {
126+ opt (c )
127+ }
128+ return c
45129}
46130
47131// RegisterBeforeAgent registers a before agent callback.
48- func (c * Callbacks ) RegisterBeforeAgent (cb BeforeAgentCallback ) * Callbacks {
49- c .BeforeAgent = append (c .BeforeAgent , cb )
132+ // Supports both old and new callback function signatures.
133+ // Old signatures are automatically wrapped into new signatures.
134+ func (c * Callbacks ) RegisterBeforeAgent (cb any ) * Callbacks {
135+ switch callback := cb .(type ) {
136+ case BeforeAgentCallbackStructured :
137+ c .BeforeAgent = append (c .BeforeAgent , callback )
138+ case BeforeAgentCallback :
139+ wrapped := func (ctx context.Context , args * BeforeAgentArgs ) (* BeforeAgentResult , error ) {
140+ // Call old signature
141+ resp , err := callback (ctx , args .Invocation )
142+ if err != nil {
143+ return nil , err
144+ }
145+ if resp != nil {
146+ return & BeforeAgentResult {CustomResponse : resp }, nil
147+ }
148+ return & BeforeAgentResult {}, nil // Return empty result to indicate callback was executed.
149+ }
150+ c .BeforeAgent = append (c .BeforeAgent , wrapped )
151+ default :
152+ panic ("unsupported callback type" )
153+ }
50154 return c
51155}
52156
53157// RegisterAfterAgent registers an after agent callback.
54- func (c * Callbacks ) RegisterAfterAgent (cb AfterAgentCallback ) * Callbacks {
55- c .AfterAgent = append (c .AfterAgent , cb )
158+ // Supports both old and new callback function signatures.
159+ // Old signatures are automatically wrapped into new signatures.
160+ func (c * Callbacks ) RegisterAfterAgent (cb any ) * Callbacks {
161+ switch callback := cb .(type ) {
162+ case AfterAgentCallbackStructured :
163+ c .AfterAgent = append (c .AfterAgent , callback )
164+ case AfterAgentCallback :
165+ wrapped := func (ctx context.Context , args * AfterAgentArgs ) (* AfterAgentResult , error ) {
166+ // Call old signature
167+ resp , err := callback (ctx , args .Invocation , args .Error )
168+ if err != nil {
169+ return nil , err
170+ }
171+ if resp != nil {
172+ return & AfterAgentResult {CustomResponse : resp }, nil
173+ }
174+ return & AfterAgentResult {}, nil // Return empty result to indicate callback was executed.
175+ }
176+ c .AfterAgent = append (c .AfterAgent , wrapped )
177+ default :
178+ panic ("unsupported callback type" )
179+ }
56180 return c
57181}
58182
183+ // handleCallbackError processes callback error and returns whether to continue.
184+ func (c * Callbacks ) handleCallbackError (err error , firstErr * error ) (shouldStop bool ) {
185+ if err == nil {
186+ return false
187+ }
188+ if ! c .continueOnError {
189+ return true
190+ }
191+ if * firstErr == nil {
192+ * firstErr = err
193+ }
194+ return false
195+ }
196+
197+ // processCallbackResult processes callback result and updates context.
198+ // Returns whether to stop execution immediately.
199+ func (c * Callbacks ) processCallbackResult (
200+ result * BeforeAgentResult ,
201+ ctx * context.Context ,
202+ lastResult * * BeforeAgentResult ,
203+ ) (shouldStop bool ) {
204+ if result == nil {
205+ return false
206+ }
207+ if result .Context != nil {
208+ * ctx = result .Context
209+ }
210+ if result .CustomResponse != nil {
211+ * lastResult = result
212+ if ! c .continueOnResponse {
213+ return true
214+ }
215+ } else {
216+ * lastResult = result
217+ }
218+ return false
219+ }
220+
221+ // finalizeBeforeAgentResult determines the final return value for before agent callbacks.
222+ func (c * Callbacks ) finalizeBeforeAgentResult (
223+ lastResult * BeforeAgentResult ,
224+ firstErr error ,
225+ ) (* BeforeAgentResult , error ) {
226+ if lastResult != nil && lastResult .CustomResponse != nil {
227+ if c .continueOnError && firstErr != nil {
228+ return lastResult , firstErr
229+ }
230+ return lastResult , nil
231+ }
232+ if c .continueOnError && firstErr != nil {
233+ return lastResult , firstErr
234+ }
235+ if lastResult != nil && lastResult .Context == nil && lastResult .CustomResponse == nil {
236+ return nil , nil
237+ }
238+ return lastResult , nil
239+ }
240+
59241// RunBeforeAgent runs all before agent callbacks in order.
60- // Returns (customResponse, error) .
61- // If any callback returns a custom response, stop and return .
242+ // This method uses the new structured callback interface .
243+ // If a callback returns a non-nil Context in the result, it will be used for subsequent callbacks .
62244func (c * Callbacks ) RunBeforeAgent (
63245 ctx context.Context ,
64- invocation * Invocation ,
65- ) (* model.Response , error ) {
246+ args * BeforeAgentArgs ,
247+ ) (* BeforeAgentResult , error ) {
248+ var lastResult * BeforeAgentResult
249+ var firstErr error
250+
66251 for _ , cb := range c .BeforeAgent {
67- customResponse , err := cb (ctx , invocation )
68- if err != nil {
252+ result , err := cb (ctx , args )
253+
254+ if c .handleCallbackError (err , & firstErr ) {
69255 return nil , err
70256 }
71- if customResponse != nil {
72- return customResponse , nil
257+
258+ if c .processCallbackResult (result , & ctx , & lastResult ) {
259+ if c .continueOnError && firstErr != nil {
260+ return result , firstErr
261+ }
262+ return result , nil
263+ }
264+ }
265+
266+ return c .finalizeBeforeAgentResult (lastResult , firstErr )
267+ }
268+
269+ // processAfterCallbackResult processes after callback result and updates context.
270+ // Returns whether to stop execution immediately.
271+ func (c * Callbacks ) processAfterCallbackResult (
272+ result * AfterAgentResult ,
273+ ctx * context.Context ,
274+ lastResult * * AfterAgentResult ,
275+ ) (shouldStop bool ) {
276+ if result == nil {
277+ return false
278+ }
279+ if result .Context != nil {
280+ * ctx = result .Context
281+ }
282+ if result .CustomResponse != nil {
283+ * lastResult = result
284+ if ! c .continueOnResponse {
285+ return true
73286 }
287+ } else {
288+ * lastResult = result
74289 }
75- return nil , nil
290+ return false
291+ }
292+
293+ // finalizeAfterAgentResult determines the final return value for after agent callbacks.
294+ func (c * Callbacks ) finalizeAfterAgentResult (
295+ lastResult * AfterAgentResult ,
296+ firstErr error ,
297+ ) (* AfterAgentResult , error ) {
298+ if lastResult != nil && lastResult .CustomResponse != nil {
299+ if c .continueOnError && firstErr != nil {
300+ return lastResult , firstErr
301+ }
302+ return lastResult , nil
303+ }
304+ if c .continueOnError && firstErr != nil {
305+ return lastResult , firstErr
306+ }
307+ if lastResult != nil && lastResult .Context == nil && lastResult .CustomResponse == nil {
308+ return nil , nil
309+ }
310+ return lastResult , nil
76311}
77312
78313// RunAfterAgent runs all after agent callbacks in order.
79- // Returns (customResponse, error) .
80- // If any callback returns a custom response, stop and return .
314+ // This method uses the new structured callback interface .
315+ // If a callback returns a non-nil Context in the result, it will be used for subsequent callbacks .
81316func (c * Callbacks ) RunAfterAgent (
82317 ctx context.Context ,
83- invocation * Invocation ,
84- runErr error ,
85- ) (* model.Response , error ) {
318+ args * AfterAgentArgs ,
319+ ) (* AfterAgentResult , error ) {
320+ var lastResult * AfterAgentResult
321+ var firstErr error
322+
86323 for _ , cb := range c .AfterAgent {
87- customResponse , err := cb (ctx , invocation , runErr )
88- if err != nil {
324+ result , err := cb (ctx , args )
325+
326+ if c .handleCallbackError (err , & firstErr ) {
89327 return nil , err
90328 }
91- if customResponse != nil {
92- return customResponse , nil
329+
330+ if c .processAfterCallbackResult (result , & ctx , & lastResult ) {
331+ if c .continueOnError && firstErr != nil {
332+ return result , firstErr
333+ }
334+ return result , nil
93335 }
94336 }
95- return nil , nil
337+
338+ return c .finalizeAfterAgentResult (lastResult , firstErr )
96339}
0 commit comments