@@ -112,8 +112,14 @@ func (i *BlockingInterception) ProcessRequest(w http.ResponseWriter, r *http.Req
112112
113113 for {
114114 // TODO add outer loop span (https://github.com/coder/aibridge/issues/67)
115+
116+ // Rebuilt per iteration: i.reqPayload mutates when an agentic
117+ // continuation appends tool results, so withBody must reflect
118+ // the latest payload on every upstream call.
119+ callOpts := []option.RequestOption {i .withBody ()}
120+
115121 var keyAttempts int
116- resp , keyAttempts , err = i .newMessage (ctx , svc )
122+ resp , keyAttempts , err = i .newMessage (ctx , svc , callOpts )
117123 totalKeyAttempts += keyAttempts
118124 if err != nil {
119125 if eventstream .IsConnError (err ) {
@@ -352,24 +358,23 @@ func (i *BlockingInterception) ProcessRequest(w http.ResponseWriter, r *http.Req
352358// newMessage routes by credential type, returning the upstream message, the
353359// number of key attempts made for this call, and any error. Centralized
354360// credentials fail over across the key pool, while BYOK makes a single attempt.
355- func (i * BlockingInterception ) newMessage (ctx context.Context , svc anthropic.MessageService ) (* anthropic.Message , int , error ) {
361+ func (i * BlockingInterception ) newMessage (ctx context.Context , svc anthropic.MessageService , opts []option. RequestOption ) (* anthropic.Message , int , error ) {
356362 switch i .cred .Kind () {
357363 case intercept .CredentialKindCentralized :
358- return i .newMessageWithKeyFailover (ctx , svc )
364+ return i .newMessageWithKeyFailover (ctx , svc , opts )
359365 case intercept .CredentialKindBYOK :
360- msg , err := i .newMessageWithKey (ctx , svc )
366+ msg , err := i .newMessageWithKey (ctx , svc , opts ... )
361367 return msg , 0 , err
362368 default :
363369 return nil , 0 , xerrors .New ("no credential configured" )
364370 }
365371}
366372
367373// newMessageWithKey performs a single upstream call.
368- func (i * BlockingInterception ) newMessageWithKey (ctx context.Context , svc anthropic.MessageService , extraOpts ... option.RequestOption ) (_ * anthropic.Message , outErr error ) {
374+ func (i * BlockingInterception ) newMessageWithKey (ctx context.Context , svc anthropic.MessageService , opts ... option.RequestOption ) (_ * anthropic.Message , outErr error ) {
369375 _ , span := i .tracer .Start (ctx , "Intercept.ProcessRequest.Upstream" , trace .WithAttributes (tracing .InterceptionAttributesFromContext (ctx )... ))
370376 defer tracing .EndSpanErr (span , & outErr )
371377
372- opts := append ([]option.RequestOption {i .withBody ()}, extraOpts ... )
373378 return svc .New (ctx , anthropic.MessageNewParams {}, opts ... )
374379}
375380
@@ -378,12 +383,12 @@ func (i *BlockingInterception) newMessageWithKey(ctx context.Context, svc anthro
378383// 429 and permanent on 401/403. Errors that aren't key-specific don't trigger
379384// failover and are returned to the caller. It returns the upstream message,
380385// the number of key attempts made for this call, and any error.
381- func (i * BlockingInterception ) newMessageWithKeyFailover (ctx context.Context , svc anthropic.MessageService ) (* anthropic.Message , int , error ) {
386+ func (i * BlockingInterception ) newMessageWithKeyFailover (ctx context.Context , svc anthropic.MessageService , opts []option. RequestOption ) (* anthropic.Message , int , error ) {
382387 centralized , ok := intercept .AsCentralized (i .cred )
383388 if ! ok {
384389 // Centralized but pool-less: Bedrock, which signs via AWS. A single
385390 // attempt with no failover.
386- msg , err := i .newMessageWithKey (ctx , svc )
391+ msg , err := i .newMessageWithKey (ctx , svc , opts ... )
387392 return msg , 0 , err
388393 }
389394 walker := centralized .Pool .Walker ()
@@ -396,12 +401,14 @@ func (i *BlockingInterception) newMessageWithKeyFailover(ctx context.Context, sv
396401 i .logger .Debug (ctx , "using centralized api key" ,
397402 slog .F ("credential_hint" , i .cred .Hint ()), slog .F ("credential_length" , i .cred .Length ()))
398403
399- msg , err := i .newMessageWithKey (ctx , svc ,
404+ requestOpts := append ([]option.RequestOption {}, opts ... )
405+ requestOpts = append (requestOpts ,
400406 option .WithAPIKey (key .Value ()),
401407 // Disable SDK retries because the failover loop
402408 // handles retries via key rotation.
403409 option .WithMaxRetries (0 ),
404410 )
411+ msg , err := i .newMessageWithKey (ctx , svc , requestOpts ... )
405412 // Key-specific failure: try the next key.
406413 if i .markKeyOnError (ctx , key , err ) {
407414 continue
0 commit comments