diff --git a/agent.go b/agent.go index 6d2d62dfb3f363491fde42eab4959069a66bead9..e05e7b536902fd75788db6ff1304ec4410f52e60 100644 --- a/agent.go +++ b/agent.go @@ -1240,7 +1240,9 @@ func (a *agent) processStepStream(ctx context.Context, stream StreamResponse, op case StreamPartTypeReasoningDelta: if active, exists := activeReasoningContent[part.ID]; exists { active.content += part.Delta - active.options = part.ProviderMetadata + if part.ProviderMetadata != nil { + active.options = part.ProviderMetadata + } activeReasoningContent[part.ID] = active } if opts.OnReasoningDelta != nil { diff --git a/providers/openrouter/language_model_hooks.go b/providers/openrouter/language_model_hooks.go index 5ae579d3ddb3fb226c62717f225b840807210b42..3390a075e3701413932409d587370bcf324ceb2f 100644 --- a/providers/openrouter/language_model_hooks.go +++ b/providers/openrouter/language_model_hooks.go @@ -188,6 +188,7 @@ type currentReasoningState struct { metadata *openai.ResponsesReasoningMetadata googleMetadata *google.ReasoningMetadata googleText string + format string } func extractReasoningContext(ctx map[string]any) *currentReasoningState { @@ -291,6 +292,7 @@ func languageModelStreamExtra(chunk openaisdk.ChatCompletionChunk, yield func(fa } } + currentState.format = detail.Format ctx[reasoningStartedCtx] = currentState delta := detail.Summary if strings.HasPrefix(detail.Format, "google-gemini") { @@ -304,6 +306,10 @@ func languageModelStreamExtra(chunk openaisdk.ChatCompletionChunk, yield func(fa }) } if len(reasoningData.ReasoningDetails) == 0 { + // Anthropic sends the signature after tool_calls, so don't end reasoning early + if strings.HasPrefix(currentState.format, "anthropic-claude") { + return ctx, true + } // this means its a model different from openai/anthropic that ended reasoning if choice.Delta.Content != "" || len(choice.Delta.ToolCalls) > 0 { ctx[reasoningStartedCtx] = nil