v3.5.0
Quality of life improvement update
Added
- New typed exceptions for specific provider failures:
ConnectionException(network),ModelNotFoundException(unknown model),InvalidRequestException(bad request), andServerException(provider error). - xAI now supports the
CodeInterpreterprovider tool. voice.route_middlewareconfig to protect the voice routes, e.g.['auth:sanctum', 'throttle:60,1'].ProviderException::responseBody()andrawResponse()expose the provider's raw error response for debugging, without unwrapping the previous exception.- Provider request events (
ProviderRequestStarted/Completed/Failed/Retrying) now carry acorrelationId(stable across retries) plusproviderandmodel, so you can trace and attribute every HTTP call.
Changed
atlas.embeddings.dimensionsis now sent to OpenAI'stext-embedding-3-small/-largeas thedimensionsparameter, so reduced-dimension (Matryoshka) embeddings work straight from the config key. An explicitdimensionsprovider option still wins; models that don't support it (e.g.text-embedding-ada-002) are unaffected.- A single
catch (ProviderException)now handles every provider failure. Catch a subclass for specific cases. - "Overloaded" provider responses are now retried, like rate limits.
- Attaching an unsupported provider-native tool (e.g. xAI's X Search on OpenAI) now throws
UnsupportedFeatureExceptionup front instead of failing at the provider.
Fixed
- Structured output via the
Schemabuilder now works on OpenAI, xAI, and OpenAI-compatible providers (Ollama, LM Studio) — previously these 400'd because strict JSON-schema requirements (additionalProperties: false, all keys required, optionals as nullable) weren't applied. Optional fields now round-trip as nullable. - Rate limits and transient server errors now retry automatically.
- Per-call and global timeouts are honored, including on queued requests.
- Network failures and mid-stream errors now surface as exceptions instead of returning truncated, successful-looking responses.
- Interrupted streams broadcast the usage and finish reason captured before the break, so cost tracking survives.
- Queued requests fail fast on unrecoverable errors (bad key, bad request, unknown model) instead of burning every retry.
- Per-request middleware (
->withMiddleware()) now runs on queued image, audio, video, speech, embedding, moderation, rerank, music, and sound-effect requests — previously it was silently dropped on queue (text and agent requests were unaffected). - Queuing a request with a closure middleware now fails fast with a clear error instead of crashing the worker; use class-based middleware for queued requests.
- Batch embeddings are realigned by the provider's
indexfield rather than response order, so an out-of-order batch can't attach the wrong vector to a chunk. - A mismatched embedding dimension now fails at embed time with a clear message (actual vs configured size, plus the fix) instead of a cryptic database error at write time.
- Changing config at runtime and calling
AtlasConfig::refresh()now applies to facade calls (Atlas::embed(), etc.) — previously the facade held a stale manager built from the old config, so runtime provider/model/dimension changes were silently ignored on facade-driven paths (embeddings, chunking). - Google tool calls missing a function name now degrade gracefully instead of erroring mid-parse.
- Listing models and voices now reports failures like any other call.
- Error messages now carry the provider's real reason across all providers — including auth failures (401/403), which previously showed a generic message instead of the provider's text (e.g. "Incorrect API key provided").
- Queued failures cap their broadcast error so it can't exceed the socket limit and drop the event.
Migration
No breaking changes for most users. Check these only if they apply:
- You hard-coded a
vector(3072)column fortext-embedding-3-largebut leftATLAS_EMBEDDING_DIMENSIONSat the 1536 default — that key is now forwarded to the API, so set it to3072to match your column. (Sizing the column fromconfig('atlas.embeddings.dimensions')already handles this.) - You catch specific exceptions (auth, rate-limit) before
ProviderException— keep thosecatchblocks first;ProviderExceptionnow catches them too. - You use
reasoning_timeout— removed. Set a longer per-call timeout instead. - You read streamed responses — mid-stream errors now throw. Wrap your read loop in try/catch.
- You attach provider-native tools to providers that don't support them — now throws
UnsupportedFeatureExceptionup front.
Pull Requests
- @timothymarois in #42
- @timothymarois in #43
- @timothymarois in #44
- @timothymarois in #45
- @timothymarois in #46
- @timothymarois in #47
- @timothymarois in #48
Full Changelog: v3.4.0...v3.5.0