Repository navigation
fix(core): run session HTTP hooks on the AI SDK route - #50487
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
session.http.request/session.http.responsenever fired for models served through AI SDK packages.packages/aialready hands the middleware to every route; the AI SDK route in core dropped it on the floor.LLMClient.stream(request, { http }) compile(request) route.streamPrepared(prepared, request, runtime, options) native route (Anthropic, OpenAIChat, …) HttpTransport.execute RequestExecutor.execute(request, options.http) ← hooks fire ai-sdk route (packages/core/src/aisdk.ts) - streamLanguage(language, prepared) ← options ignored + streamLanguage(language, prepared, options.http) language.doStream(callOptions) options.fetch(input, init) ← SDK-owned fetch - send(input, init) + middleware ? throughMiddleware(...) : send(input, init)Fallout today: the Copilot
http.requesthook only saw Claude; the Azure (Entra bearer) and Snowflake (conversation complete400 → synthetic SSE stop) hooks were dead code.Why not just pass the middleware to
fetch?The SDK's
fetchis fixed at construction and the language model is cached per catalog model. The middleware is per request. So the wrapper cannot close over it; it has to be looked up per call.The middleware rides through
AsyncLocalStoragefor the duration ofdoStream:One request through the wrapper
sequenceDiagram participant SDK as AI SDK model participant W as fetch wrapper (aisdk.ts) participant M as http middleware (model-request.ts) participant H as plugin hooks participant Net as upstream fetch SDK->>W: fetch(url, init{string body}) W->>W: Request → HttpClientRequest, body → Uint8Array W->>M: http(request, handler) M->>H: session.http.request H-->>M: mutated Request M->>W: handler(sent) W->>W: HttpClientRequest → (url, init) W->>Net: send(url, init) Net-->>W: Response W-->>M: HttpClientResponse.fromWeb M->>H: session.http.response H-->>M: mutated Response M-->>W: HttpClientResponse W->>W: stream + status + headers → Response W-->>SDK: ResponseNotes on the edges:
HttpClientRequest.toWebandclone().text()freely (the real middleware inmodel-request.tsdoes exactly that). Matches the native route, where bodies are never a single-use stream.Effect.runPromiseWith(callerContext), so spans stay attached, and it takes the SDK's abort signal.fetchagain per attempt; each attempt goes through the middleware, same as the native executor.send(input, init)path, unchanged.Also
http.request.packages/ai.Testing
New in
packages/core/test/aisdk.test.ts, both against a real@ai-sdk/openai-compatiblemodel viaLLMClient.generate:POST …/chat/completions, reads the body twice, setsx-hook; upstream fetch receivesx-hookalongside the SDK'sAuthorization: Bearer test; a rewritten SSE response is what the stream yields.bun testacrossaisdk,aisdk-native,session-model-request-hooks,session-runner, and the Copilot/Azure/OpenAI plugin tests: 265 pass.bun run checkclean.Live verification
Standalone dev server from this branch, a throwaway
.opencode/pluginsplugin registeringhttp.request/http.responseforgithub-copilot, real requests to CAPI:/responses/responsesconversation-background, 200/v1/messagesBold rows were unreachable by these hooks before this change.