Sync AI updates and fix the Doctum deployment
Merged
BI
2d ago+2500+2500+0123456789012345678901234567890123456789-319-319-012345678901234567890123456789
Laravel Updates
- AI failure events — Dispatch failure events for audio, image, transcription and reranking requests, file uploads and store changes, as embeddings and classification already did. Each failed attempt dispatches one, including attempts that failover recovers from, while errors thrown by success listeners don't. As with Hypervel's other optional events, failure events are skipped when nothing listens for them. Document the new events.
- Interrupted tool results — Port the regression test for the interrupted tool results replayed after a failed turn, and use upstream's named arguments. Hypervel already kept the result ID that OpenAI, Azure OpenAI and xAI use to pair tool output with its call.
- Conversation model keys — Port the test that conversations and messages created through Eloquent keep their string keys. Hypervel's conversation models already use UUID keys, so upstream's Laravel 12 source change isn't needed.
- Classifying images — Classify image attachments with TypeSafe and OpenRouter. OpenAI and OpenAI-compatible classification label images given string keys, so questions can refer to them by name. Port the Classifying Images documentation, adapted for these providers and named images, and add the missing table of contents entries.
- AI workbench dependencies — Update source-map-js to 1.2.2 in the AI workbench.
- Provider resource IDs — Encode file, store and document IDs as a single URL path segment in the OpenAI, Azure OpenAI, Anthropic, OpenRouter and Gemini gateways, and reject empty and dot-segment IDs. An ID containing
/,?,#or..could otherwise reach a different endpoint, such as a file ID that deletes a vector store. - Raw file responses — Keep the provider's raw HTTP response on stored and retrieved files, so metadata such as a file's size is available. The response is dropped when the file is serialized. Document it.
- Image response escaping — Escape the MIME type and image data in
ImageResponse::toHtml(). Only the alt text was escaped, so quotes in either could break out of thesrcattribute. - Schema normalizer limit — Adopt upstream's 10,000-node limit and message for shared
$refexpansion in the schema normalizer. Hypervel already capped each call at 20,000 nodes, and the lower limit bounds the same work. MCP tools still drop the parameters of a schema over the limit. - Anthropic server tool text — Separate the text Anthropic writes before and after a server tool, such as a web search, with a blank line, while keeping text split by citations joined. Streamed and final text match.
- Structured prompt responses — Give
prompt()andagent()conditional return types, so static analysis accepts array access on a structured agent's response. Structured agents always return aStructuredAgentResponse, with an empty payload when a run produces none, such as a stopped run or a Bedrock approval pause. Document the empty structured value. - Gemini metadata filters — Validate Gemini file search metadata filter keys and wrap every condition in parentheses, since AIP-160 gives OR higher precedence than AND. Hypervel already escaped values and supported
whereNotIn(). Document Gemini's key rules.
Additional Hypervel Fixes
- Deploy the Doctum API docs with
pnpm dlx wrangler@4. The Cloudflare action installed Wrangler withpnpm addat the workspace root, which pnpm 12 rejects, so every deployment since the pnpm 12 upgrade failed. - Correct the classification state types, which only allowed string keys, so static analysis rejected the list states classification supports. Add a type fixture.
- Reject a TypeSafe structured state with an attachment named
imagesalongside unnamed attachments. Upstream replaced that attachment's reference with the unnamed images but still uploaded it. - Keep integer keys, such as record IDs, in a TypeSafe structured state when images are attached. Upstream merged the images with an array spread, which renumbered those keys.
- Ignore empty Anthropic text blocks after a server tool. Upstream added a blank line for them that the model never wrote and the stream never sent.
- Return a structured response for agents with an empty schema too. Upstream returned a plain
AgentResponsefor them, contradicting the declared return type. Their text remains available. - Handle empty Gemini
whereIn()andwhereNotIn()lists, which produced an invalid empty group. An empty exclusion list is omitted, and an empty inclusion list throws before the request, since Gemini has no portable condition that matches nothing. - Ask contributors reconciling upstream changes to explain the non-obvious Hypervel behavior they keep, so later updates can see why it exists.
The affected suites, formatting and static analysis pass. CI will run the full suite and supported service matrix.
Summary by cubic
Syncs upstream Laravel AI updates to Hypervel's AI SDK and fixes the broken Doctum deployment.
The deployment failure: the Cloudflare action installed Wrangler with pnpm add at the workspace root, which pnpm 12 rejects, so every deployment has failed since the pnpm 12 upgrade. It now runs pnpm dlx wrangler@4 instead.
AI changes
- Dispatches failure events for audio, image, transcription, reranking, classification, embedding, file upload, and store changes, once per failed attempt including attempts failover recovers from.
- Classifies image attachments with TypeSafe and OpenRouter; OpenAI and OpenAI-compatible classification label images given string keys so questions can refer to them by name.
- Encodes file, store, and document IDs as single URL path segments in the OpenAI, Azure OpenAI, Anthropic, OpenRouter, and Gemini gateways, and rejects empty and dot-segment IDs.
- Keeps the provider's raw HTTP response on stored and retrieved files so metadata like file size is available; dropped when serialized.
- Escapes MIME type and image data in
ImageResponse::toHtml(). - Adopts upstream's 10,000-node schema normalizer limit for shared
$refexpansion. - Separates Anthropic text written before and after a server tool with a blank line, while keeping citation-split text joined.
- Gives
prompt()andagent()conditional return types so structured agents always return aStructuredAgentResponse. - Validates Gemini file search metadata filter keys, wraps every condition in parentheses, and handles empty
whereIn()andwhereNotIn()lists. - Corrects classification state types, rejects a TypeSafe structured state with an attachment named
imagesalongside unnamed attachments, and asks contributors reconciling upstream changes to explain non-obvious Hypervel behavior. - Updates
source-map-jsto 1.2.2 in the AI workbench and documents the new events and behaviors.
[!NOTE]
Add image classification attachments, failure events, and resource ID encoding to AI gateways
- Classification gateways (OpenAI, OpenRouter, TypeSafe) now accept image attachments. Named attachments are sent with a text label before the image; OpenRouter uses base64 data URLs and TypeSafe uses referenced assets via the new
ResolvesInlineImages concern.
- All AI operations (classification, embeddings, image, audio, transcription, reranking, file, and store operations) now dispatch a matching failure event before rethrowing, through the shared
Provider::dispatchingFailure helper.
- File and store gateways now encode resource IDs through the new
EncodesResourceIds concern, so IDs with path or query characters stay within one URL segment. FileResponse and StoredFileResponse retain the raw provider HTTP response.
- Structured-output agents now always return
StructuredAgentResponse, with empty structured data when no structured output was produced. Anthropic text after tool blocks gets a two-newline separator.
- Risk:
SchemaNormalizer node budget drops from 20,000 to 10,000, so schemas that previously normalized now throw; GeminiProvider::formatMetadataCondition is removed and Gemini metadata filters now reject empty inclusion lists and invalid keys; ImageResponse::toHtml now escapes MIME type and image data.
Macroscope summarized b44851e.