on main help sign in
fix(agent): implement OnRetry logging with structured retry fields (#2700)
commit549f717263b3fc4520ec1fede5009ddc951db057 authoriceymoss <114280774+iceymoss@users.noreply.github.com> dateFri 24 Apr 2026, 20:40 (6 months ago) parentf669bd3
2 files changed+50−1
internal/agent/agent.go+18−1 internal/agent/agent_test.go+32
internal/agent/agent.go+18−1
@@ -378,7 +378,7 @@ func (a *sessionAgent) Run(ctx context.Context, call SessionAgentCall) (*fantasy
378378 return a.messages.Update(ctx, *currentAssistant)
379379 },
380380 OnRetry: func(err *fantasy.ProviderError, delay time.Duration) {
381− // TODO: implement
381+ slog.Warn("Provider request failed, retrying", providerRetryLogFields(err, delay)...)
382382 },
383383 OnToolCall: func(tc fantasy.ToolCallContent) error {
384384 toolCall := message.ToolCall{
@@ -1318,3 +1318,20 @@ func buildSummaryPrompt(todos []session.Todo) string {
13181318 }
13191319 return sb.String()
13201320}
1321+
1322+func providerRetryLogFields(err *fantasy.ProviderError, delay time.Duration) []any {
1323+ fields := []any{
1324+ "retry_delay", delay.String(),
1325+ }
1326+ if err == nil {
1327+ return fields
1328+ }
1329+ fields = append(fields, "status_code", err.StatusCode)
1330+ if err.Title != "" {
1331+ fields = append(fields, "title", err.Title)
1332+ }
1333+ if err.Message != "" {
1334+ fields = append(fields, "message", err.Message)
1335+ }
1336+ return fields
1337+}
internal/agent/agent_test.go+32
@@ -8,6 +8,7 @@ import (
88 "runtime"
99 "strings"
1010 "testing"
11+ "time"
1112
1213 "charm.land/fantasy"
1314 "charm.land/x/vcr"
@@ -793,3 +794,34 @@ func TestPreparePrompt_OrphanedToolUseMixed(t *testing.T) {
793794 }
794795 require.Equal(t, 1, syntheticCount, "expected exactly one synthetic result for the orphaned call")
795796}
797+
798+func TestProviderRetryLogFields(t *testing.T) {
799+ t.Run("nil provider error", func(t *testing.T) {
800+ fields := providerRetryLogFields(nil, 2*time.Second)
801+ require.Equal(t, []any{"retry_delay", "2s"}, fields)
802+ })
803+
804+ t.Run("provider error with title and message", func(t *testing.T) {
805+ fields := providerRetryLogFields(&fantasy.ProviderError{
806+ StatusCode: 429,
807+ Title: "rate limit",
808+ Message: "too many requests",
809+ }, 1500*time.Millisecond)
810+ require.Equal(t, []any{
811+ "retry_delay", "1.5s",
812+ "status_code", 429,
813+ "title", "rate limit",
814+ "message", "too many requests",
815+ }, fields)
816+ })
817+
818+ t.Run("provider error without optional strings", func(t *testing.T) {
819+ fields := providerRetryLogFields(&fantasy.ProviderError{
820+ StatusCode: 503,
821+ }, time.Second)
822+ require.Equal(t, []any{
823+ "retry_delay", "1s",
824+ "status_code", 503,
825+ }, fields)
826+ })
827+}