diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ea73e6..78cc102 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +### Fixed + +- Corrected `runImage()` documentation to distinguish validation exceptions: + values other than `ImageModelError` propagate to the caller, while + `ImageModelError` follows the normal retry and model-fallback rules. + Exhausted model failures still resolve as a failed result. + ## 1.21.0 - 2026-09-25 ### Added diff --git a/docs/image-generation.md b/docs/image-generation.md index 9dfed98..fb2e83d 100644 --- a/docs/image-generation.md +++ b/docs/image-generation.md @@ -68,8 +68,11 @@ For each model in `chain`, in order: (default 60000), or exhausted retries, move to the next model. When every model fails, `runImage()` resolves with `status: 'failed'` and the -last error; it does not throw. It rejects only for invalid options or when the -caller's `signal` aborts, in which case no further model is tried. +last error; it does not throw. It rejects for invalid options, when the caller's +`signal` aborts, or when `validate` throws a value other than `ImageModelError`. +A non-`ImageModelError` validation exception stops the chain. An +`ImageModelError` thrown by `validate` is handled as an attempt failure and +follows the same retry and model-fallback rules described above. Adapters must not resubmit a generation internally. Every billable provider call is one attempt, so the attempt records show the real number of calls and @@ -124,7 +127,10 @@ Return `{ ok: true }` to accept, or `{ ok: false, reason }` to refuse. A refusal is recorded as a `rejected` attempt with type `invalid_output`, and the refused image is kept on the record as `rejectedOutput` so it can be reviewed. Set `retryable: false` on the refusal to skip the remaining retries of that model. -An exception thrown by `validate` propagates out of `runImage()`. +An exception thrown by `validate` propagates out of `runImage()` unless it is +an `ImageModelError`. That error is recorded as a `failed` attempt and follows +the normal retry and model-fallback rules; it does not necessarily reject the +run. ## Attempt records diff --git a/packages/core/src/image/run-image.ts b/packages/core/src/image/run-image.ts index 329d5f5..62397f0 100644 --- a/packages/core/src/image/run-image.ts +++ b/packages/core/src/image/run-image.ts @@ -102,8 +102,10 @@ async function callWithDeadline( * above `maxRetryAfterMs`, moves on to the next model. * * Returns a result rather than throwing when every model fails, so the attempt - * records are always available. Rejects only for invalid options or when - * `signal` aborts. + * records are always available. Rejects for invalid options, when `signal` + * aborts, or when the caller's `validate` callback throws a value other than + * `ImageModelError`. An `ImageModelError` thrown by `validate` is recorded as + * an attempt failure and follows the normal retry and model-fallback rules. */ export async function runImage(options: RunImageOptions): Promise { const maxRetries = options.maxRetriesPerModel ?? DEFAULT_MAX_RETRIES_PER_MODEL