refactor(ai): avoid redundant request body validation (#52909)

This commit is contained in:
Aiden Cline
2026-10-03 22:22:15 -05:00
committed by GitHub
Unverified
parent 0048c97c27
commit 5617044dc9
6 changed files with 44 additions and 88 deletions
@@ -1,7 +1,7 @@
import { Effect, Schema } from "effect"
import { Protocol } from "../route/protocol.js"
import { OpenResponses } from "./open-responses.js"
import { JsonObject, optionalArray, ProviderShared } from "./shared.js"
import { JsonObject, ProviderShared } from "./shared.js"
import { OpenResponsesOptions } from "./utils/open-responses-options.js"
import { ResponsesHostedTools } from "./utils/responses-hosted-tools.js"
@@ -25,8 +25,6 @@ const WebExtractorItem = Schema.StructWithRest(
)
const Body = Schema.Struct({
...OpenResponses.coreFields,
input: Schema.Array(Schema.Union([OpenResponses.InputItem, WebExtractorItem])),
tools: optionalArray(Schema.Union([OpenResponses.Tool, NativeTool])),
enable_thinking: Options.fields.enableThinking,
previous_response_id: Options.fields.previousResponseId,
conversation: Options.fields.conversation,
@@ -52,7 +50,7 @@ export const protocol = Protocol.make({
const opts = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Options))(req.providerOptions ?? {})
const body = yield* OpenResponses.fromRequestWithAdapter(req, adapter)
const choice = body.tool_choice
return yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Body))({
return {
...body,
enable_thinking: opts.enableThinking,
previous_response_id: opts.previousResponseId,
@@ -62,7 +60,7 @@ export const protocol = Protocol.make({
typeof choice === "object" && choice.type === "function"
? { type: "allowed_tools" as const, mode: "required" as const, tools: [choice] }
: choice,
})
}
}),
},
stream: {
+5 -27
View File
@@ -1,6 +1,5 @@
import { Effect, Encoding, Schema } from "effect"
import { Protocol } from "../route/protocol.js"
import { HttpTransport } from "../route/transport/index.js"
import { LLMEvent, LLMRequest, Message, ToolResultPart } from "../schema/index.js"
import { OpenResponses } from "./open-responses.js"
import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js"
@@ -44,13 +43,6 @@ const ImageItem = Schema.Struct({
error: Schema.optional(Schema.Unknown),
})
const Body = Schema.Struct({
...OpenResponses.coreFields,
input: Schema.Array(Schema.Union([OpenResponses.InputItem, ImageItem])),
tools: optionalArray(Schema.Union([OpenResponses.Tool, NativeTool])),
stream: Schema.Literal(true),
})
const MessageAnnotations = Schema.Struct({
content: Schema.Array(Schema.Struct({ annotations: optionalArray(JsonObject) })),
})
@@ -62,12 +54,13 @@ interface ParserState extends OpenResponses.ParserState {
const adapter = {
id: ADAPTER,
name: NAME,
nativeTool: (native) => ProviderShared.validateWith(Schema.decodeUnknownEffect(NativeTool))(native.meta),
restoreHostedToolItem: (item: unknown) => (Schema.is(ImageItem)(item) ? item : undefined),
} satisfies OpenResponses.ProviderAdapter
const fromRequest = Effect.fn("MetaResponses.fromRequest")(function* (request: LLMRequest) {
const key = request.model.route.providerMetadataKey ?? String(request.model.provider)
const projected = ProviderShared.flattenToolRequest(
return yield* OpenResponses.fromRequestWithAdapter(
LLMRequest.update(request, {
messages: request.messages.map((message) =>
Message.make({
@@ -93,23 +86,8 @@ const fromRequest = Effect.fn("MetaResponses.fromRequest")(function* (request: L
}),
),
}),
adapter,
)
return yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Body))({
...(yield* OpenResponses.lowerConversation(projected.request, adapter)),
...OpenResponses.lowerGeneration(request),
tools:
projected.tools.length === 0
? undefined
: yield* Effect.forEach(projected.tools, (tool) =>
Effect.gen(function* () {
if (tool.native === undefined) return yield* OpenResponses.lowerTool(NAME, tool)
return yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(NativeTool))(tool.native.meta)
}),
),
tool_choice:
OpenResponses.allowedToolChoice(request) ??
(request.toolChoice ? yield* OpenResponses.lowerToolChoice(NAME, request.toolChoice) : undefined),
})
})
const HOSTED_TOOLS = {
@@ -222,7 +200,7 @@ const step = Effect.fn("MetaResponses.step")(function* (state: ParserState, inpu
export const protocol = Protocol.make({
id: ADAPTER,
body: { schema: Body, from: fromRequest },
body: { schema: OpenResponses.OpenResponsesBody, from: fromRequest },
stream: {
event: OpenResponses.protocol.stream.event,
initial: (request): ParserState => ({ ...OpenResponses.initial(request, adapter), completedItems: new Set() }),
@@ -231,6 +209,6 @@ export const protocol = Protocol.make({
},
})
export const httpTransport = HttpTransport.sseJson.with<Schema.Schema.Type<typeof Body>>()
export const httpTransport = OpenResponses.httpTransport
export * as MetaResponses from "./meta-responses.js"
+29 -30
View File
@@ -168,10 +168,20 @@ export const ConfigurationUpdate = Schema.Struct({
type: Schema.Literal("configuration_update"),
reasoning: Schema.Struct({ effort: OpenResponsesOptions.ReasoningEffort }),
})
type ConfigurationUpdate = Schema.Schema.Type<typeof ConfigurationUpdate>
export type ConfigurationUpdate = Schema.Schema.Type<typeof ConfigurationUpdate>
export const HostedToolReplay = Schema.StructWithRest(
Schema.Struct({
type: Schema.String,
id: Schema.String,
}),
[JsonObject],
)
export type HostedToolReplayItem = Schema.Schema.Type<typeof HostedToolReplay>
export const InputItem = Schema.Union([
CompactionItem,
ConfigurationUpdate,
Schema.Struct({ type: Schema.tag("message"), role: Schema.tag("system"), content: Schema.String }),
Schema.Struct({ type: Schema.tag("message"), role: Schema.tag("developer"), content: Schema.String }),
Schema.Struct({
@@ -204,24 +214,9 @@ export const InputItem = Schema.Union([
output: OpenResponsesFunctionCallOutput,
}),
HostedToolItem,
HostedToolReplay,
])
type OpenResponsesInputItem = Schema.Schema.Type<typeof InputItem>
export type HostedToolReplayItem = {
readonly type: string
readonly id: string
readonly [key: string]: unknown
}
type LoweredInputItem =
| OpenResponsesInputItem
| HostedToolReplayItem
| ConfigurationUpdate
| {
readonly type: "message"
readonly id?: string
readonly role: "assistant"
readonly content: ReadonlyArray<{ readonly type: "output_text"; readonly text: string }>
readonly phase?: MessagePhase | null
}
// Mutable counterpart of the schema reasoning item so `lowerMessages` can fold
// multiple streamed summary parts into the same item before flushing.
@@ -239,6 +234,14 @@ export const Tool = Schema.Struct({
strict: Schema.optional(Schema.Boolean),
})
export const HostedTool = Schema.StructWithRest(
Schema.Struct({
type: Schema.String,
}),
[JsonObject],
)
export type HostedTool = Schema.Schema.Type<typeof HostedTool>
export const ToolChoice = Schema.Union([
Schema.Literals(["auto", "none", "required"]),
Schema.Struct({ type: Schema.tag("function"), name: Schema.String }),
@@ -257,7 +260,7 @@ export const coreFields = {
model: Schema.String,
input: Schema.Array(InputItem),
instructions: Schema.optional(Schema.String),
tools: optionalArray(Tool),
tools: optionalArray(Schema.Union([Tool, HostedTool])),
tool_choice: Schema.optional(ToolChoice),
store: Schema.optional(Schema.Boolean),
metadata: Schema.optional(Schema.Record(Schema.String, Schema.String)),
@@ -292,7 +295,7 @@ export const coreFields = {
frequency_penalty: Schema.optional(Schema.Number),
}
const OpenResponsesBody = Schema.Struct({
export const OpenResponsesBody = Schema.Struct({
...coreFields,
stream: Schema.Literal(true),
})
@@ -397,9 +400,7 @@ export const decodeChannelEvent = (frame: string) =>
export interface ProviderAdapter {
readonly id: string
readonly name: string
readonly nativeTool?: (
native: NonNullable<ToolDefinition["native"]>,
) => Effect.Effect<{ readonly type: string }, AIError>
readonly nativeTool?: (native: NonNullable<ToolDefinition["native"]>) => Effect.Effect<HostedTool, AIError>
readonly lowerMedia?: (input: {
readonly part: MediaPart
readonly media: Media.Inline | undefined
@@ -455,8 +456,10 @@ export const lowerTool = Effect.fn("OpenResponses.lowerTool")(function* (protoco
})
export const lowerTools = (tools: ReadonlyArray<ToolDefinition>, adapter: ProviderAdapter) =>
Effect.forEach(tools, (tool) =>
tool.native !== undefined && adapter.nativeTool ? adapter.nativeTool(tool.native) : lowerTool(adapter.name, tool),
Effect.forEach(
tools,
(tool): Effect.Effect<Schema.Schema.Type<typeof Tool> | HostedTool, AIError> =>
tool.native !== undefined && adapter.nativeTool ? adapter.nativeTool(tool.native) : lowerTool(adapter.name, tool),
)
export const lowerToolChoice = (protocolName: string, toolChoice: NonNullable<LLMRequest["toolChoice"]>) =>
@@ -592,7 +595,7 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (
request: LLMRequest,
adapter: ProviderAdapter,
) {
const input: LoweredInputItem[] = []
const input: OpenResponsesInputItem[] = []
const providerMetadataKey = metadataKey(request.model)
for (const message of request.messages) {
@@ -827,11 +830,7 @@ export const fromRequestWithAdapter = Effect.fn("OpenResponses.fromRequestWithAd
}
})
const decodeBody = ProviderShared.validateWith(Schema.decodeUnknownEffect(OpenResponsesBody))
export const fromRequest = Effect.fn("OpenResponses.fromRequest")(function* (request: LLMRequest) {
return yield* decodeBody(yield* fromRequestWithAdapter(request, BASE_ADAPTER))
})
export const fromRequest = (request: LLMRequest) => fromRequestWithAdapter(request, BASE_ADAPTER)
// =============================================================================
// Stream Parsing
+3 -12
View File
@@ -103,15 +103,8 @@ const OpenAIResponsesToolChoice = Schema.Union([
Schema.Struct({ type: Schema.tag("image_generation") }),
])
const OpenAIResponsesInputItem = Schema.Union([
OpenResponses.InputItem,
OpenAIResponsesHostedToolItem,
OpenResponses.ConfigurationUpdate,
])
const OpenAIResponsesCoreFields = {
...OpenResponses.coreFields,
input: Schema.Array(OpenAIResponsesInputItem),
tools: optionalArray(OpenAIResponsesTools),
tool_choice: Schema.optional(OpenAIResponsesToolChoice),
context_management: Schema.optional(
@@ -134,7 +127,7 @@ export type OpenAIResponsesBody = Schema.Schema.Type<typeof OpenAIResponsesBody>
export const CompactionTrigger = Schema.Struct({ type: Schema.Literal("compaction_trigger") })
const CheckpointBody = Schema.Struct({
...OpenAIResponsesBody.fields,
input: Schema.Array(Schema.Union([OpenAIResponsesInputItem, CompactionTrigger])),
input: Schema.Array(Schema.Union([OpenResponses.InputItem, CompactionTrigger])),
})
const adapter = {
@@ -202,15 +195,13 @@ const lowerToolChoice = (toolChoice: NonNullable<LLMRequest["toolChoice"]>, tool
: { type: "function" as const, name },
})
const decodeBody = ProviderShared.validateWith(Schema.decodeUnknownEffect(OpenAIResponsesBody))
const fromRequest = Effect.fn("OpenAIResponses.fromRequest")(function* (request: LLMRequest) {
const management = yield* ProviderShared.validateWith(
Schema.decodeUnknownEffect(Schema.UndefinedOr(ContextManagement)),
)(request.providerOptions?.contextManagement)
const options = OpenResponsesOptions.resolve(request)
const updates = resolveEffortUpdates(request, options.reasoningEffort)
return yield* decodeBody({
return {
...(yield* OpenResponses.lowerConversation(updates.request, adapter)),
...OpenResponses.lowerGeneration(request, { ...options, reasoningEffort: updates.effort }),
context_management: management?.map((edit) => ({ type: edit.type, compact_threshold: edit.compactThreshold })),
@@ -220,7 +211,7 @@ const fromRequest = Effect.fn("OpenAIResponses.fromRequest")(function* (request:
? undefined
: (OpenResponses.allowedToolChoice(request) ??
(request.toolChoice ? yield* lowerToolChoice(request.toolChoice, request.tools) : undefined)),
})
}
})
const checkpointBody = {
+2 -9
View File
@@ -31,19 +31,12 @@ const XAIResponsesHostedToolItem = Schema.Union([
),
])
const XAIResponsesBody = Schema.Struct({
...OpenResponses.coreFields,
input: Schema.Array(Schema.Union([OpenResponses.InputItem, XAIResponsesHostedToolItem])),
stream: Schema.Literal(true),
})
const adapter = {
id: ADAPTER,
name: NAME,
restoreHostedToolItem: (item: unknown) => (Schema.is(XAIResponsesHostedToolItem)(item) ? item : undefined),
} satisfies OpenResponses.ProviderAdapter
const decodeBody = ProviderShared.validateWith(Schema.decodeUnknownEffect(XAIResponsesBody))
const fromRequest = Effect.fn("XAIResponses.fromRequest")(function* (request: LLMRequest) {
if (request.providerOptions?.contextManagement !== undefined)
return yield* ProviderShared.unsupportedOperation({
@@ -53,7 +46,7 @@ const fromRequest = Effect.fn("XAIResponses.fromRequest")(function* (request: LL
message:
"xAI requires explicit compaction through LLMClient.compact; automatic context management is not supported",
})
return yield* decodeBody(yield* OpenResponses.fromRequestWithAdapter(request, adapter))
return yield* OpenResponses.fromRequestWithAdapter(request, adapter)
})
const HOSTED_TOOLS = {
@@ -83,7 +76,7 @@ const step = (state: OpenResponses.ParserState, input: OpenResponses.Event) => {
export const protocol = Protocol.make({
id: ADAPTER,
body: {
schema: XAIResponsesBody,
schema: OpenResponses.OpenResponsesBody,
from: fromRequest,
},
stream: {
+2 -5
View File
@@ -358,7 +358,6 @@ function makeFromTransport<Body, Prepared, Frame, Event, State>(
input: MakeTransportInput<Body, Prepared, Frame, Event, State>,
): Route<Body, Prepared> {
const protocol = input.protocol
const encodeBody = Schema.encodeSync(Schema.fromJsonString(protocol.body.schema))
const decodeEventEffect = Schema.decodeUnknownEffect(protocol.stream.event)
const decodeEvent = (route: string) => (frame: Frame) =>
decodeEventEffect(frame).pipe(
@@ -417,7 +416,7 @@ function makeFromTransport<Body, Prepared, Frame, Event, State>(
request,
endpoint: routeInput.endpoint,
auth: routeInput.auth ?? Auth.none,
encodeBody,
encodeBody: ProviderShared.encodeJson,
middleware: options?.http,
webSocket: options?.webSocket,
}),
@@ -576,9 +575,7 @@ const compile = Effect.fn("LLM.compile")(function* (request: LLMRequest, options
const resolved = prepareRequest(request)
const route = resolved.model.route
const body = yield* route.body
.from(resolved)
.pipe(Effect.flatMap(ProviderShared.validateWith(Schema.decodeUnknownEffect(route.body.schema))))
const body = yield* route.body.from(resolved)
const prepared = yield* route.prepareTransport(body, resolved, options)
return {