fix: retry a rate-limited turn instead of failing the caller - #18
EthanThatOneKid wants to merge 1 commit into
Conversation
Data's agent answers from a free-tier provider, so a turn can come back as a provider rate limit rather than an answer. The provider never ran that turn, so nothing was written to the conversation or to memory, and the service turned the limit into a 502 for whoever asked. Wait and re-run the same turn in place instead: honor the provider's own retryDelay when it names one, back off otherwise, and cap the wait. Only explicitly retryable provider failures are retried, so a missing conversation, an unconfigured provider, a timeout, or any other failure is untouched, and the wait plus the retry must fit inside DATA_LETTA_TIMEOUT_MS or the retry is skipped rather than overrunning the turn. Measured before the change: two consecutive POST /ask calls returned 502 with 'google 429 Too Many Requests, retryDelay 45s', and a turn against the same agent answered normally 41s later, so the limit is transient and the caller was failing on a minute the provider was busy. Verified against a stub letta, all three paths: a 429 followed by success answers and costs two child processes; a durable 429 returns the error after two retries; a non-retryable failure is not retried at all.
|
This pull request modifies how Data handles rate-limited responses from its agent provider. Previously, a rate-limited turn would result in a
Reviewers should start by examining |
1 similar comment
|
This pull request modifies how Data handles rate-limited responses from its agent provider. Previously, a rate-limited turn would result in a
Reviewers should start by examining |
|
Superseded by #22. Data now runs as a Zo persona reached directly by the Discord bridge, so the retry this adds has no caller: |
…24) PR #18 was closed as superseded, and its closing comment said the reasoning would be carried forward in this directory's README. It was not: the archive documented the runtime and left the retry out entirely, so the branch was the only copy of the work and the last non-main ref on the remote. Keep the artifact, drop it from the live tree. The module still has no caller now that Data answers through /zo/ask, so it moves to `archives/letta-brain/lib/` with the archived service that wired it, and the archive README records what it did, why it was never deployed, and what makes it worth keeping: read the delay the provider named, and bound it. The archive now typechecks and its test runs (`archives/**/*.ts` in tsconfig, `archives/*/lib/*.test.ts` in the test glob), so what is kept here is verified rather than inert. Co-authored-by: zocomputer <help@zocomputer.com>
Data is up but not answering.
POST /askreturns502whenever the agent's provider rate-limits the turn, and the provider is a free tier, so that happens on ordinary traffic.Measured
Two consecutive live turns on 2026-09-26:
A turn against the same agent answered normally at
06:06:51, ~41s after the last502, so the limit is per-minute capacity, not a broken key or a durable daily cap. The caller eats a hard failure on a minute the provider is busy.Change
A rate-limited turn never reached the model, so nothing was written to the conversation or to memory, and the same question can be asked again. The service now waits and re-runs that turn in place, honoring the provider's own
retryDelaywhen it names one and backing off otherwise, up toDATA_PROVIDER_RETRY_ATTEMPTS(default 2), with a cap ofDATA_PROVIDER_RETRY_CAP_MS(default 60000) on any single wait.The retry re-runs the same session, so a recovered turn continues the conversation the caller already had; it never starts a new one.
Three guards, so this cannot paper over a real error:
isRetryableProviderFailure). A missing conversation, an unconfigured provider, a timeout, and any other failure are untouched.DATA_LETTA_TIMEOUT_MS; otherwise the retry is skipped and the original error is returned as-is.DATA_LETTA_TIMEOUT_MSstill retries rather than refusing to.What this does not fix
Data still has one provider and no second one, so a durable outage — an exhausted daily quota, or a provider that stays down — still fails the turn. That decision is
wazootech/data#9item 5.Verification
npm run typecheckclean,npm test34/34 (8 new inlib/provider-retry.test.ts).End to end against a stub
letta, all three paths:200with the answer502with the provider's errorProvider is not configured502, no retry logged