diff --git a/packages/ai/src/route/client.ts b/packages/ai/src/route/client.ts index f4873f347ef..a126122e79e 100644 --- a/packages/ai/src/route/client.ts +++ b/packages/ai/src/route/client.ts @@ -5,7 +5,7 @@ import { Endpoint, type EndpointPatch } from "./endpoint" import { RequestExecutor } from "./executor" import { Framing } from "./framing" import { HttpTransport } from "./transport" -import type { HttpMiddleware, Transport, TransportRuntime } from "./transport" +import type { HttpMiddleware, HttpRequestTransform, Transport, TransportRuntime } from "./transport" import { WebSocketExecutor } from "./transport" import type { Protocol } from "./protocol" import { applyCachePolicy } from "../cache-policy" @@ -155,6 +155,7 @@ export interface Interface { } export interface StreamOptions { + readonly transform?: HttpRequestTransform readonly http?: HttpMiddleware } @@ -307,6 +308,7 @@ function makeFromTransport( auth: routeInput.auth ?? Auth.none, encodeBody, headers: routeInput.headers, + transform: options?.transform, middleware: options?.http, }), streamPrepared: (prepared: Prepared, request: LLMRequest, runtime: TransportRuntime) => { diff --git a/packages/ai/src/route/index.ts b/packages/ai/src/route/index.ts index eb9f1759c55..598da97e4ad 100644 --- a/packages/ai/src/route/index.ts +++ b/packages/ai/src/route/index.ts @@ -23,4 +23,11 @@ export type { ApiKeyMode, AuthOverride, ProviderAuthOption } from "./auth-option export type { Definition as EndpointFn, EndpointInput } from "./endpoint" export type { Definition as FramingDef } from "./framing" export type { Protocol as ProtocolDef } from "./protocol" -export type { HttpHandler, HttpMiddleware, Transport as TransportDef, TransportRuntime } from "./transport" +export type { + HttpHandler, + HttpMiddleware, + HttpRequest, + HttpRequestTransform, + Transport as TransportDef, + TransportRuntime, +} from "./transport" diff --git a/packages/ai/src/route/transport/http.ts b/packages/ai/src/route/transport/http.ts index 223afb353c1..3ebd3511e5a 100644 --- a/packages/ai/src/route/transport/http.ts +++ b/packages/ai/src/route/transport/http.ts @@ -75,10 +75,12 @@ export const httpJson = (input: HttpJsonInput): HttpJs prepare: (prepareInput) => Effect.gen(function* () { const parts = yield* jsonRequestParts({ ...prepareInput }) + const transformed = { url: parts.url, method: "POST", headers: { ...parts.headers }, body: parts.bodyText } + yield* prepareInput.transform?.(transformed) ?? Effect.void const request = ProviderShared.jsonPost({ - url: parts.url, - body: parts.bodyText, - headers: parts.headers, + url: transformed.url, + body: transformed.body ?? "", + headers: Headers.fromInput(transformed.headers), }) return { request, diff --git a/packages/ai/src/route/transport/index.ts b/packages/ai/src/route/transport/index.ts index c74e578e8bb..f9dcf00eab9 100644 --- a/packages/ai/src/route/transport/index.ts +++ b/packages/ai/src/route/transport/index.ts @@ -10,6 +10,15 @@ export interface TransportRuntime { readonly webSocket?: WebSocketExecutorInterface } +export interface HttpRequest { + url: string + readonly method: string + headers: Record + body: string | undefined +} + +export type HttpRequestTransform = (request: HttpRequest) => Effect.Effect + export interface Transport { readonly id: string readonly prepare: (input: TransportPrepareInput) => Effect.Effect @@ -23,6 +32,7 @@ export interface TransportPrepareInput { readonly auth: Auth.Definition readonly encodeBody: (body: Body) => string readonly headers?: (input: { readonly request: LLMRequest }) => Record + readonly transform?: HttpRequestTransform readonly middleware?: HttpMiddleware }