Skip to content

[EPIC-V][LOU-V7.2] createAgent retry policy, fallbackModels and typed retry/fallback events - #86

Merged
LinuxDevil merged 2 commits into
mainfrom
lou-v7-2-agent-retry-fallback
Oct 1, 2026
Merged

LinuxDevil merged 2 commits into
mainfrom
lou-v7-2-agent-retry-fallback

Conversation

@LinuxDevil

Copy link
Copy Markdown
Owner

What

  • createAgent({ retry?: WithRetryOptions | false, fallbackModels?: readonly string[] }).
    • Models given as provider/model strings, or picked from the environment, are resolved with maxRetries: 0 and wrapped in withRetry(…, retry ?? { maxRetries: 2 }). retry: false skips the wrapper.
    • fallbackModels are resolved with the same resolver (resolveProviderSpec, with errors prefixed createAgent:) when the agent is created. They are composed as withFallback([withRetry(primary), withRetry(fb1), ...]).
    • A user-supplied provider is wrapped only when retry is set. It keeps its own retry behaviour otherwise. fallbackModels work with it too.
  • aiSdkProvider passes maxRetries: config.maxRetries ?? 2 to generateText/streamText. Direct users keep the ai default of 2. Providers that createAgent resolves get 0, so retries happen in one place and can be observed.
  • New typed events in src/execution/agentEvents.ts:
    • provider.retry: { attempt, maxRetries, delayMs, error: { message, category? }, provider }. category is left out when it is 'unknown'.
    • provider.fallback: { from, to, error: { message } }.
    • Both are exported from the package root.
  • Routing: a streaming run's sink (agentRun.ts) attaches a listener to each model request. It is keyed by a symbol and lives in the internal module src/providers/providerEvents.ts, which is not re-exported. withRetry/withFallback report to the listener of the call they handle, in addition to their own onRetry/onFallback.
    • The events therefore reach agent.stream(), session.stream(), AgentExecutor.stream() and sub-agent streams (tagged with subagent), for any withRetry/withFallback provider.
    • AgentExecutor.ts is unchanged, and send() behaves as before.
  • Schema version stays 1. docs/streaming.md says new event types are additive and do not change v.
  • Docs: configuration.md and providers.md document both options with type-checked snippets, and streaming.md lists the two events and where they come in the step order. llms-full.txt is regenerated, and CHANGELOG.md has an Added entry plus a Changed entry on retries no longer stacking.

Why

LOU-V7.2 (Epic V) brings this to parity with eve's gateway fallbacks and open-harness withRetry, and finishes LOU-V7. Before this change the ai SDK's 2 internal retries ran inside every withRetry attempt (retries stacked), and you could not see retries or fallbacks from a run.

Acceptance criteria

  • createAgent({ retry, fallbackModels }): retry defaults to { maxRetries: 2 } (same number of calls as today's implicit ai retries) and false disables it. fallbackModels are resolved with the same resolver and tried in order via withFallback. Works with model strings and with a user-supplied provider.
  • Stacked retries fixed: aiSdkProvider passes config.maxRetries ?? 2, and createAgent-wrapped providers get 0. This is in the CHANGELOG.
  • Typed provider.retry / provider.fallback events are emitted into agent.stream() and session.stream(). send() callers read the result as before. AgentExecutor.ts is not modified, and the schema version is not bumped because the change is additive.
  • Docs (configuration.md, providers.md, streaming.md) and the regenerated llms files are included, and the CHANGELOG has an entry.
  • Tests in the new src/createAgentResilience.test.ts (8 tests):
    • a retry followed by success emits provider.retry and returns the final result (stream() and send());
    • retry: false fails fast;
    • fallbackModels switches models and emits provider.fallback;
    • the events show up in agent.stream() and session.stream();
    • a provider instance is retried only when retry is set.
  • Also: an aiSdkProvider unit test on the maxRetries it passes to the ai calls, and type tests for the new events in agentEvents.test-d.ts.
  • src/context/*, src/flows/*, src/react/* and src/execution/resume.ts are not touched.

How verified

  • npx tsc --noEmit exited 0.
  • npm run lint reported 0 errors, 433 warnings. The changed files add no new warnings; the only warning in them is the existing as any in aiSdkProvider.ts.
  • npm run test:types reported Type Errors no errors.
  • npx vitest run: Test Files 141 passed (141), Tests 2065 passed | 3 skipped (2068).
  • npm run build && npm run build --workspace=packages/create-loushy-agent exited 0.
  • npm run docs:verify-snippets -- --skip-build: all 97 snippet(s) type-check against src/; 6 also run cleanly.
  • npm run docs:llms:check exited 0.
  • npm run test:coverage (141 files, 2065 tests passed) and npm run fallow both exited 0. Fallow reports No issues found; the only duplicate it lists was already in untouched files (OpenAIProvider.ts/AnthropicProvider.ts).

🤖 Generated with Claude Code

https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR


Generated by Claude Code

claude added 2 commits October 1, 2026 14:11
…allback events

- createAgent({ retry?: WithRetryOptions | false, fallbackModels?: string[] }):
  model strings (and env-picked models) are resolved with maxRetries: 0 and
  wrapped in withRetry (default { maxRetries: 2 }); fallbackModels are
  resolved the same way and composed with withFallback. A provider instance
  is wrapped only when retry is set.
- aiSdkProvider passes maxRetries to generateText/streamText
  (config.maxRetries ?? 2), so retries no longer stack under createAgent.
- New typed stream events provider.retry and provider.fallback. A streaming
  run attaches a per-call listener to each request (symbol-keyed, internal
  providers/providerEvents.ts); withRetry/withFallback report to it, so the
  events reach agent.stream(), session.stream(), AgentExecutor.stream() and
  sub-agent streams without touching AgentExecutor. Schema version stays 1.
- Docs (configuration, providers, streaming), llms-full.txt, CHANGELOG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR
…fallback

# Conflicts:
#	docs/streaming.md
#	llms-full.txt
@LinuxDevil
LinuxDevil merged commit 83f2b83 into main Oct 1, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants