From 140224b0fcaea9e8d7cb2ba04ec0c23da16b15ef Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Thu, 2 Jul 2026 00:23:46 -0500 Subject: [PATCH] fix(core): derive models.dev reasoning variants (#34726) --- packages/client/src/generated/types.ts | 5 + packages/core/src/catalog.ts | 1 + packages/core/src/config/plugin/provider.ts | 5 +- packages/core/src/config/provider.ts | 1 + packages/core/src/model.ts | 7 +- packages/core/src/plugin/models-dev.ts | 84 ++++++++++--- packages/core/src/plugin/provider/opencode.ts | 2 +- packages/core/src/plugin/variant.ts | 3 +- packages/core/src/provider.ts | 10 +- packages/core/src/session/runner/model.ts | 10 ++ .../plugin/fixtures/models-dev-reasoning.json | 67 +++++++++++ packages/core/test/plugin/host.ts | 4 +- packages/core/test/plugin/models-dev.test.ts | 111 ++++++++++-------- .../test/plugin/provider-opencode.test.ts | 6 +- packages/core/test/plugin/variant.test.ts | 8 +- .../core/test/session-runner-model.test.ts | 27 +++-- .../llm/src/protocols/anthropic-messages.ts | 37 +++++- packages/llm/src/protocols/openai-chat.ts | 4 - .../llm/src/protocols/openai-responses.ts | 2 - .../llm/src/protocols/utils/openai-options.ts | 22 ++-- packages/llm/src/schema/ids.ts | 2 +- .../test/provider/anthropic-messages.test.ts | 17 +++ .../llm/test/provider/openai-chat.test.ts | 18 ++- .../test/provider/openai-responses.test.ts | 10 ++ packages/schema/src/agent.ts | 2 +- packages/schema/src/model.ts | 2 +- packages/schema/src/provider.ts | 8 +- packages/sdk/js/src/v2/gen/sdk.gen.ts | 43 +++++++ packages/sdk/js/src/v2/gen/types.gen.ts | 60 ++++++++++ 29 files changed, 458 insertions(+), 120 deletions(-) create mode 100644 packages/core/test/plugin/fixtures/models-dev-reasoning.json diff --git a/packages/client/src/generated/types.ts b/packages/client/src/generated/types.ts index 1999704bcc..9783526c82 100644 --- a/packages/client/src/generated/types.ts +++ b/packages/client/src/generated/types.ts @@ -151,6 +151,7 @@ export type AgentListOutput = { readonly id: string readonly model?: { readonly id: string; readonly providerID: string; readonly variant?: string } readonly request: { + readonly settings: { readonly [x: string]: JsonValue } readonly headers: { readonly [x: string]: string } readonly body: { readonly [x: string]: JsonValue } } @@ -2192,12 +2193,14 @@ export type ModelListOutput = { readonly output: ReadonlyArray } readonly request: { + readonly settings: { readonly [x: string]: JsonValue } readonly headers: { readonly [x: string]: string } readonly body: { readonly [x: string]: JsonValue } readonly variant?: string } readonly variants: ReadonlyArray<{ readonly id: string + readonly settings: { readonly [x: string]: JsonValue } readonly headers: { readonly [x: string]: string } readonly body: { readonly [x: string]: JsonValue } }> @@ -2256,6 +2259,7 @@ export type ProviderListOutput = { } | { readonly type: "native"; readonly url?: string; readonly settings: { readonly [x: string]: JsonValue } } readonly request: { + readonly settings: { readonly [x: string]: JsonValue } readonly headers: { readonly [x: string]: string } readonly body: { readonly [x: string]: JsonValue } } @@ -2289,6 +2293,7 @@ export type ProviderGetOutput = { } | { readonly type: "native"; readonly url?: string; readonly settings: { readonly [x: string]: JsonValue } } readonly request: { + readonly settings: { readonly [x: string]: JsonValue } readonly headers: { readonly [x: string]: string } readonly body: { readonly [x: string]: JsonValue } } diff --git a/packages/core/src/catalog.ts b/packages/core/src/catalog.ts index ec3eed83d7..78688ce12c 100644 --- a/packages/core/src/catalog.ts +++ b/packages/core/src/catalog.ts @@ -85,6 +85,7 @@ const layer = Layer.effect( ? { ...model.api, settings: { ...provider.api.settings, ...model.api.settings } } : model.api const request = { + settings: { ...provider.request.settings, ...model.request.settings }, headers: { ...provider.request.headers, ...model.request.headers }, body: { ...provider.request.body, ...model.request.body }, variant: model.request.variant, diff --git a/packages/core/src/config/plugin/provider.ts b/packages/core/src/config/plugin/provider.ts index 6f6e0528da..d8ec3a58ae 100644 --- a/packages/core/src/config/plugin/provider.ts +++ b/packages/core/src/config/plugin/provider.ts @@ -4,7 +4,6 @@ import { define } from "../../plugin/internal" import { Effect } from "effect" import { Config } from "../../config" import { ModelV2 } from "../../model" -import { ProviderV2 } from "../../provider" export const Plugin = define({ id: "config-provider", @@ -54,6 +53,7 @@ export const Plugin = define({ if (item.name !== undefined) provider.name = item.name if (item.api !== undefined) provider.api = { ...item.api } if (item.request !== undefined) { + Object.assign(provider.request.settings, item.request.settings) Object.assign(provider.request.headers, item.request.headers) Object.assign(provider.request.body, item.request.body) } @@ -71,6 +71,7 @@ export const Plugin = define({ } } if (config.request !== undefined) { + Object.assign(model.request.settings, config.request.settings) Object.assign(model.request.headers, config.request.headers) Object.assign(model.request.body, config.request.body) if (config.request.variant !== undefined) model.request.variant = config.request.variant @@ -81,11 +82,13 @@ export const Plugin = define({ if (!existing) { existing = { id: variant.id, + settings: {}, headers: {}, body: {}, } model.variants.push(existing) } + Object.assign(existing.settings, variant.settings) Object.assign(existing.headers, variant.headers) Object.assign(existing.body, variant.body) } diff --git a/packages/core/src/config/provider.ts b/packages/core/src/config/provider.ts index 1b54757078..c78d7979ae 100644 --- a/packages/core/src/config/provider.ts +++ b/packages/core/src/config/provider.ts @@ -5,6 +5,7 @@ import { ProviderV2 } from "../provider" import { ModelV2 } from "../model" export class Request extends Schema.Class("ConfigV2.Provider.Request")({ + settings: ProviderV2.Settings.pipe(Schema.optional), headers: Schema.Record(Schema.String, Schema.String).pipe(Schema.optional), body: Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.optional), }) {} diff --git a/packages/core/src/model.ts b/packages/core/src/model.ts index 52fff98733..41abbcb2d2 100644 --- a/packages/core/src/model.ts +++ b/packages/core/src/model.ts @@ -26,8 +26,13 @@ export type Api = Model.Api export const Info = Model.Info export type Info = Model.Info -export type MutableInfo = Omit, "api"> & { +export type MutableRequest = ProviderV2.MutableRequest & { variant?: string } +export type MutableVariant = ProviderV2.MutableRequest & { id: VariantID } + +export type MutableInfo = Omit, "api" | "request" | "variants"> & { api: ProviderV2.MutableApi + request: MutableRequest + variants: MutableVariant[] } export function parse(input: string): { providerID: ProviderV2.ID; modelID: ID } { diff --git a/packages/core/src/plugin/models-dev.ts b/packages/core/src/plugin/models-dev.ts index aacc3bd2c5..2ab445b248 100644 --- a/packages/core/src/plugin/models-dev.ts +++ b/packages/core/src/plugin/models-dev.ts @@ -70,25 +70,73 @@ function mergeCost(base: ModelV2Info["cost"], override: ModelsDev.Model["cost"] return [merge(baseDefault ?? { input: 0, output: 0, cache: { read: 0, write: 0 } }, nextDefault), ...tiers.values()] } -function reasoningVariants(model: ModelsDev.Model, packageName: string | undefined): ModelV2Info["variants"] { - const result = new Map() - if (packageName === "@ai-sdk/openai" || packageName === "@ai-sdk/openai-compatible") { - const option = model.reasoning_options?.find((option) => option.type === "effort") - for (const value of option?.values ?? []) { - const id = value === null ? "none" : value - if (typeof id !== "string") continue - const variantID = ModelV2.VariantID.make(id) - result.set(variantID, { - id: variantID, - headers: {}, - body: - packageName === "@ai-sdk/openai" - ? { include: ["reasoning.encrypted_content"], reasoning: { effort: id, summary: "auto" } } - : { reasoning_effort: id }, - }) +const OPENAI_INCLUDE_ENCRYPTED_REASONING = ["reasoning.encrypted_content"] + +function reasoningVariants(provider: ModelsDev.Provider, model: ModelsDev.Model): ModelV2Info["variants"] { + const npm = model.provider?.npm ?? provider.npm + const options = model.reasoning_options ?? [] + const effort = options.find((option) => option.type === "effort") + if (effort?.type === "effort") { + return effort.values.flatMap((value) => { + const raw: unknown = value + const id = raw === null ? "none" : typeof raw === "string" ? raw : undefined + if (id === undefined) return [] + const settings = settingsForEffort(npm, id) + return settings ? [{ id, settings, headers: {}, body: {} }] : [] + }) + } + + const budget = options.find((option) => option.type === "budget_tokens") + if (budget?.type === "budget_tokens") return budgetVariants(npm, budget) + + // Toggle-only reasoning is intentionally left for a follow-up because V1 has + // provider/model-specific behavior like MiniMax M3 adaptive thinking and + // Qwen/GLM enable_thinking request shapes in packages/opencode. + return [] +} + +function settingsForEffort(npm: string | undefined, effort: string): ProviderV2.Settings | undefined { + if (npm === "@openrouter/ai-sdk-provider") return { reasoning: { effort } } + if (npm === "@ai-sdk/anthropic" || npm === "@ai-sdk/google-vertex/anthropic") { + return { thinking: { type: "adaptive", display: "summarized" }, effort } + } + if (npm === "@ai-sdk/google" || npm === "@ai-sdk/google-vertex") { + return { thinkingConfig: { includeThoughts: true, thinkingLevel: effort } } + } + if (npm === "@ai-sdk/azure") return { reasoningEffort: effort } + if (npm === "@ai-sdk/openai") { + return { + reasoningEffort: effort, + reasoningSummary: "auto", + include: OPENAI_INCLUDE_ENCRYPTED_REASONING, } } - return [...result.values()] + if (npm === "@ai-sdk/openai-compatible") return { reasoningEffort: effort } +} + +function budgetVariants( + npm: string | undefined, + option: Extract[number], { type: "budget_tokens" }>, +): ModelV2Info["variants"] { + const max = option.max + const high = option.max === undefined ? Math.max(option.min ?? 0, 16_000) : Math.min(Math.max(option.min ?? 0, 16_000), option.max) + return [ + { id: "high", budget: high }, + ...(max === undefined || max === high ? [] : [{ id: "max", budget: max }]), + ].flatMap((item) => { + const settings = settingsForBudget(npm, item.budget) + return settings ? [{ id: item.id, settings, headers: {}, body: {} }] : [] + }) +} + +function settingsForBudget(npm: string | undefined, budget: number): ProviderV2.Settings | undefined { + if (npm === "@openrouter/ai-sdk-provider") return { reasoning: { max_tokens: budget } } + if (npm === "@ai-sdk/anthropic" || npm === "@ai-sdk/google-vertex/anthropic") { + return { thinking: { type: "enabled", budgetTokens: budget } } + } + if (npm === "@ai-sdk/google" || npm === "@ai-sdk/google-vertex") { + return { thinkingConfig: { includeThoughts: true, thinkingBudget: budget } } + } } function modeName(model: ModelsDev.Model, mode: string) { @@ -193,7 +241,7 @@ export const ModelsDevPlugin = define({ for (const model of Object.values(item.models)) { const baseCost = cost(model.cost) - const variants = reasoningVariants(model, model.provider?.npm ?? item.npm) + const variants = reasoningVariants(item, model) catalog.model.update(providerID, model.id, (draft) => applyModel(draft, model, { cost: baseCost, variants })) for (const [mode, options] of Object.entries(model.experimental?.modes ?? {})) { catalog.model.update(providerID, `${model.id}-${mode}`, (draft) => diff --git a/packages/core/src/plugin/provider/opencode.ts b/packages/core/src/plugin/provider/opencode.ts index 9b37b54ed1..bfa84ecd78 100644 --- a/packages/core/src/plugin/provider/opencode.ts +++ b/packages/core/src/plugin/provider/opencode.ts @@ -146,7 +146,7 @@ export const OpencodePlugin = define item.id === variantID) if (!existing) { - existing = { id: variantID, headers: {}, body: {} } + existing = { id: variantID, settings: {}, headers: {}, body: {} } model.variants.push(existing) } Object.assign(existing.headers, options.headers) diff --git a/packages/core/src/plugin/variant.ts b/packages/core/src/plugin/variant.ts index 05e1653b6e..7c6e688399 100644 --- a/packages/core/src/plugin/variant.ts +++ b/packages/core/src/plugin/variant.ts @@ -33,7 +33,8 @@ export function generate(model: ModelV2Info): ModelV2Info["variants"] { if (!["glm-5.2", "glm-5-2", "glm-5p2"].some((name) => ids.includes(name))) return [] return ["high", "max"].map((id) => ({ id, + settings: { reasoningEffort: id }, headers: {}, - body: { reasoning_effort: id }, + body: {}, })) } diff --git a/packages/core/src/provider.ts b/packages/core/src/provider.ts index 03f7d7eef3..eb443ca710 100644 --- a/packages/core/src/provider.ts +++ b/packages/core/src/provider.ts @@ -19,7 +19,15 @@ export type MutableApi = T extends Api export const Request = Provider.Request export type Request = Provider.Request +export const Settings = Provider.Settings +export type Settings = Provider.Settings + export const Info = Provider.Info export type Info = Provider.Info -export type MutableInfo = Omit, "api"> & { api: MutableApi } +export type MutableRequest = Types.DeepMutable + +export type MutableInfo = Omit, "api" | "request"> & { + api: MutableApi + request: MutableRequest +} diff --git a/packages/core/src/session/runner/model.ts b/packages/core/src/session/runner/model.ts index 7f517e0b10..5460204616 100644 --- a/packages/core/src/session/runner/model.ts +++ b/packages/core/src/session/runner/model.ts @@ -97,11 +97,20 @@ const withDefaults = (model: ModelV2.Info, route: AnyRoute) => { provider: model.providerID, endpoint: model.api.url === undefined ? undefined : { baseURL: model.api.url }, headers: model.request.headers, + providerOptions: providerOptions(model), http: { body: httpBody }, limits: { context: model.limit.context, output: model.limit.output }, }) } +const providerOptions = (model: ModelV2.Info) => { + if (Object.keys(model.request.settings).length === 0) return undefined + if (model.api.type !== "aisdk") return undefined + if (model.api.package === "@ai-sdk/openai") return { openai: model.request.settings } + if (model.api.package === "@ai-sdk/anthropic") return { anthropic: model.request.settings } + if (model.api.package === "@ai-sdk/openai-compatible") return { openai: model.request.settings } +} + export const withVariant = ( model: ModelV2.Info, variantID: ModelV2.VariantID | undefined, @@ -119,6 +128,7 @@ export const withVariant = ( return Effect.succeed( variant ? produce(model, (draft) => { + Object.assign(draft.request.settings, variant.settings) Object.assign(draft.request.headers, variant.headers) Object.assign(draft.request.body, variant.body) }) diff --git a/packages/core/test/plugin/fixtures/models-dev-reasoning.json b/packages/core/test/plugin/fixtures/models-dev-reasoning.json new file mode 100644 index 0000000000..410b7c2572 --- /dev/null +++ b/packages/core/test/plugin/fixtures/models-dev-reasoning.json @@ -0,0 +1,67 @@ +{ + "openai": { + "id": "openai", + "name": "OpenAI", + "env": ["OPENAI_API_KEY"], + "npm": "@ai-sdk/openai", + "api": "https://api.openai.com/v1", + "models": { + "gpt-reasoning": { + "id": "gpt-reasoning", + "name": "GPT Reasoning", + "release_date": "2026-01-01", + "attachment": false, + "reasoning": true, + "reasoning_options": [ + { "type": "effort", "values": ["low", "high"] }, + { "type": "budget_tokens", "min": 1024, "max": 64000 }, + { "type": "toggle" } + ], + "temperature": true, + "tool_call": true, + "limit": { "context": 128000, "output": 8192 }, + "experimental": { + "modes": { + "high": { + "provider": { + "headers": { "x-mode": "high" }, + "body": { "service_tier": "priority" } + } + } + } + } + } + } + }, + "anthropic": { + "id": "anthropic", + "name": "Anthropic", + "env": ["ANTHROPIC_API_KEY"], + "npm": "@ai-sdk/anthropic", + "api": "https://api.anthropic.com/v1", + "models": { + "claude-budget": { + "id": "claude-budget", + "name": "Claude Budget", + "release_date": "2026-01-01", + "attachment": false, + "reasoning": true, + "reasoning_options": [{ "type": "budget_tokens", "min": 1024, "max": 64000 }], + "temperature": true, + "tool_call": true, + "limit": { "context": 128000, "output": 8192 } + }, + "claude-effort": { + "id": "claude-effort", + "name": "Claude Effort", + "release_date": "2026-01-01", + "attachment": false, + "reasoning": true, + "reasoning_options": [{ "type": "effort", "values": ["low"] }], + "temperature": true, + "tool_call": true, + "limit": { "context": 128000, "output": 8192 } + } + } + } +} diff --git a/packages/core/test/plugin/host.ts b/packages/core/test/plugin/host.ts index e572d9b624..5dae04c2cf 100644 --- a/packages/core/test/plugin/host.ts +++ b/packages/core/test/plugin/host.ts @@ -288,7 +288,7 @@ function providerInfo(value: ProviderV2.MutableInfo) { return { ...value, api: { ...value.api, settings: value.api.settings && { ...value.api.settings } }, - request: { headers: { ...value.request.headers }, body: { ...value.request.body } }, + request: { settings: { ...value.request.settings }, headers: { ...value.request.headers }, body: { ...value.request.body } }, } } @@ -303,11 +303,13 @@ function modelInfo(value: ModelV2.Info | ModelV2.MutableInfo) { }, request: { ...value.request, + settings: { ...value.request.settings }, headers: { ...value.request.headers }, body: { ...value.request.body }, }, variants: value.variants.map((variant) => ({ ...variant, + settings: { ...variant.settings }, headers: { ...variant.headers }, body: { ...variant.body }, })), diff --git a/packages/core/test/plugin/models-dev.test.ts b/packages/core/test/plugin/models-dev.test.ts index 90ef94f312..b3dfe275ba 100644 --- a/packages/core/test/plugin/models-dev.test.ts +++ b/packages/core/test/plugin/models-dev.test.ts @@ -168,14 +168,14 @@ describe("ModelsDevPlugin", () => { ), ) - it.effect("derives OpenAI reasoning variants from models.dev reasoning options", () => + it.effect("converts reasoning options into settings variants", () => Effect.acquireUseRelease( Effect.sync(() => { const previous = { path: Flag.OPENCODE_MODELS_PATH, disabled: Flag.OPENCODE_DISABLE_MODELS_FETCH, } - Flag.OPENCODE_MODELS_PATH = path.join(import.meta.dir, "fixtures", "models-dev.json") + Flag.OPENCODE_MODELS_PATH = path.join(import.meta.dir, "fixtures", "models-dev-reasoning.json") Flag.OPENCODE_DISABLE_MODELS_FETCH = true return previous }), @@ -183,17 +183,6 @@ describe("ModelsDevPlugin", () => { Effect.gen(function* () { const catalog = yield* Catalog.Service const integrations = yield* Integration.Service - yield* catalog.transform((catalog) => { - catalog.model.update(ProviderV2.ID.opencode, ModelV2.ID.make("gpt-5.5"), (model) => { - model.variants = [ - { - id: ModelV2.VariantID.make("high"), - headers: { custom: "true" }, - body: { custom: true }, - }, - ] - }) - }) yield* ModelsDevPlugin.effect( host({ catalog: catalogHost(catalog), @@ -201,42 +190,67 @@ describe("ModelsDevPlugin", () => { }), ) - expect((yield* catalog.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("gpt-5.5")))?.variants).toEqual([ - { - id: ModelV2.VariantID.make("none"), - headers: {}, - body: { - include: ["reasoning.encrypted_content"], - reasoning: { effort: "none", summary: "auto" }, - }, - }, - expect.objectContaining({ - id: "low", - body: { - include: ["reasoning.encrypted_content"], - reasoning: { effort: "low", summary: "auto" }, - }, - }), - expect.objectContaining({ - id: "medium", - body: { - include: ["reasoning.encrypted_content"], - reasoning: { effort: "medium", summary: "auto" }, - }, - }), - expect.objectContaining({ - id: "high", - headers: { custom: "true" }, - body: { custom: true }, - }), - expect.objectContaining({ - id: "xhigh", - body: { - include: ["reasoning.encrypted_content"], - reasoning: { effort: "xhigh", summary: "auto" }, - }, - }), + const model = yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("gpt-reasoning")) + expect(model?.variants.map((variant) => variant.id)).toEqual([ + ModelV2.VariantID.make("low"), + ModelV2.VariantID.make("high"), ]) + expect(model?.variants).toContainEqual({ + id: ModelV2.VariantID.make("low"), + settings: { + reasoningEffort: "low", + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], + }, + headers: {}, + body: {}, + }) + expect(model?.variants).toContainEqual({ + id: ModelV2.VariantID.make("high"), + settings: { + reasoningEffort: "high", + reasoningSummary: "auto", + include: ["reasoning.encrypted_content"], + }, + headers: {}, + body: {}, + }) + + const mode = yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("gpt-reasoning-high")) + expect(mode).toMatchObject({ + id: "gpt-reasoning-high", + name: "GPT Reasoning High", + request: { + headers: { "x-mode": "high" }, + body: { service_tier: "priority" }, + }, + }) + expect(mode?.variants.map((variant) => variant.id)).toEqual([ + ModelV2.VariantID.make("low"), + ModelV2.VariantID.make("high"), + ]) + + const budgetModel = yield* catalog.model.get(ProviderV2.ID.anthropic, ModelV2.ID.make("claude-budget")) + expect(budgetModel?.variants).toContainEqual({ + id: ModelV2.VariantID.make("high"), + settings: { thinking: { type: "enabled", budgetTokens: 16000 } }, + headers: {}, + body: {}, + }) + expect(budgetModel?.variants).toContainEqual({ + id: ModelV2.VariantID.make("max"), + settings: { thinking: { type: "enabled", budgetTokens: 64000 } }, + headers: {}, + body: {}, + }) + + const anthropicEffortModel = yield* catalog.model.get(ProviderV2.ID.anthropic, ModelV2.ID.make("claude-effort")) + expect(anthropicEffortModel?.variants).toContainEqual({ + id: ModelV2.VariantID.make("low"), + settings: { thinking: { type: "adaptive", display: "summarized" }, effort: "low" }, + headers: {}, + body: {}, + }) }).pipe(Effect.provide(AppNodeBuilder.build(ModelsDev.node))), (previous) => Effect.sync(() => { @@ -245,5 +259,4 @@ describe("ModelsDevPlugin", () => { }), ), ) - }) diff --git a/packages/core/test/plugin/provider-opencode.test.ts b/packages/core/test/plugin/provider-opencode.test.ts index a3bc595ef7..7f97d686d7 100644 --- a/packages/core/test/plugin/provider-opencode.test.ts +++ b/packages/core/test/plugin/provider-opencode.test.ts @@ -142,6 +142,7 @@ describe("OpencodePlugin", () => { model.variants = [ { id: ModelV2.VariantID.make("custom"), + settings: {}, headers: { "x-custom": "true" }, body: { custom: true }, }, @@ -177,7 +178,7 @@ describe("OpencodePlugin", () => { url: `${server.url.origin}/v1`, }, }) - expect(provider.request).toEqual({ headers: { "x-org-id": "org" }, body: { custom: "value" } }) + expect(provider.request).toEqual({ settings: {}, headers: { "x-org-id": "org" }, body: { custom: "value" } }) expect(yield* (yield* Integration.Service).get(Integration.ID.make("remote"))).toBeUndefined() const model = required(yield* catalog.model.get(ProviderV2.ID.make("remote"), ModelV2.ID.make("model"))) @@ -192,11 +193,13 @@ describe("OpencodePlugin", () => { expect(model.variants).toEqual([ { id: ModelV2.VariantID.make("custom"), + settings: {}, headers: { "x-custom": "true" }, body: { custom: true }, }, { id: ModelV2.VariantID.make("high"), + settings: {}, headers: {}, body: { temperature: 0.2 }, }, @@ -359,6 +362,7 @@ describe("OpencodePlugin", () => { ...ProviderV2.Info.empty(ProviderV2.ID.opencode), api: { type: "aisdk", package: "test-provider" }, request: { + settings: {}, headers: {}, body: { apiKey: "configured" }, }, diff --git a/packages/core/test/plugin/variant.test.ts b/packages/core/test/plugin/variant.test.ts index 84af04a4df..61f392caf8 100644 --- a/packages/core/test/plugin/variant.test.ts +++ b/packages/core/test/plugin/variant.test.ts @@ -37,8 +37,8 @@ describe("VariantPlugin", () => { yield* VariantPlugin.Plugin.effect(host({ catalog: catalogHost(service) })) expect((yield* service.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("glm-5.2")))?.variants).toEqual([ - expect.objectContaining({ id: "high", body: { reasoning_effort: "high" } }), - expect.objectContaining({ id: "max", body: { reasoning_effort: "max" } }), + expect.objectContaining({ id: "high", settings: { reasoningEffort: "high" } }), + expect.objectContaining({ id: "max", settings: { reasoningEffort: "max" } }), ]) }), ) @@ -53,14 +53,14 @@ describe("VariantPlugin", () => { type: "aisdk", package: "@ai-sdk/openai-compatible", } - model.variants = [{ id: ModelV2.VariantID.make("high"), headers: { custom: "true" }, body: {} }] + model.variants = [{ id: ModelV2.VariantID.make("high"), settings: {}, headers: { custom: "true" }, body: {} }] }) }) yield* VariantPlugin.Plugin.effect(host({ catalog: catalogHost(service) })) expect((yield* service.model.get(ProviderV2.ID.opencode, ModelV2.ID.make("glm-5.2")))?.variants).toEqual([ expect.objectContaining({ id: "high", headers: { custom: "true" } }), - expect.objectContaining({ id: "max", body: { reasoning_effort: "max" } }), + expect.objectContaining({ id: "max", settings: { reasoningEffort: "max" } }), ]) }), ) diff --git a/packages/core/test/session-runner-model.test.ts b/packages/core/test/session-runner-model.test.ts index 77c1bd3d3a..6d11bd877a 100644 --- a/packages/core/test/session-runner-model.test.ts +++ b/packages/core/test/session-runner-model.test.ts @@ -30,6 +30,7 @@ const model = (api: Api, variants: ModelV2.Info["variants"] = []) => api: { id: ModelV2.ID.make("api-test-model"), ...api }, capabilities: { tools: true, input: ["text"], output: ["text"] }, request: { + settings: {}, headers: { "x-test": "header" }, body: { apiKey: "secret", custom_extension: { enabled: true } }, }, @@ -83,7 +84,7 @@ describe("SessionRunnerModel", () => { url: "https://compatible.example/v1", settings: { apiKey: "settings-secret", compatibility: "strict" }, }), - request: { headers: {}, body: {} }, + request: { settings: {}, headers: {}, body: {} }, }), ) const request = LLM.request({ model: resolved, prompt: "Hello" }) @@ -100,17 +101,17 @@ describe("SessionRunnerModel", () => { }), ) - it.effect("overlays selected OpenAI Session variant bodies", () => + it.effect("overlays selected OpenAI Session variant settings and bodies", () => Effect.gen(function* () { const catalog = model({ type: "aisdk", package: "@ai-sdk/openai", url: "https://openai.example/v1" }, [ { id: ModelV2.VariantID.make("high"), + settings: { reasoningEffort: "high" }, headers: { "x-variant": "high" }, body: { store: false, service_tier: "priority", temperature: 0.2, - reasoning: { effort: "high" }, }, }, ]) @@ -137,7 +138,9 @@ describe("SessionRunnerModel", () => { store: false, service_tier: "priority", temperature: 0.2, - reasoning: { effort: "high" }, + }) + expect(resolved.route.defaults.providerOptions).toEqual({ + openai: { store: false, reasoningEffort: "high" }, }) }), ) @@ -149,6 +152,7 @@ describe("SessionRunnerModel", () => { [ { id: ModelV2.VariantID.make("high"), + settings: {}, headers: {}, body: { store: false, reasoning_effort: "high" }, }, @@ -205,13 +209,14 @@ describe("SessionRunnerModel", () => { }), ) - it.effect("overlays selected Anthropic Session variant bodies", () => + it.effect("overlays selected Anthropic Session variant settings", () => Effect.gen(function* () { const catalog = model({ type: "aisdk", package: "@ai-sdk/anthropic", url: "https://anthropic.example/v1" }, [ { id: ModelV2.VariantID.make("high"), + settings: { thinking: { type: "enabled", budgetTokens: 12000 } }, headers: {}, - body: { thinking: { type: "enabled", budget_tokens: 12000 } }, + body: {}, }, ]) const session = SessionV2.Info.make({ @@ -229,7 +234,9 @@ describe("SessionRunnerModel", () => { expect(resolved.route.defaults.http?.body).toEqual({ custom_extension: { enabled: true }, - thinking: { type: "enabled", budget_tokens: 12000 }, + }) + expect(resolved.route.defaults.providerOptions).toEqual({ + anthropic: { thinking: { type: "enabled", budgetTokens: 12000 } }, }) }), ) @@ -252,7 +259,7 @@ describe("SessionRunnerModel", () => { const resolved = yield* SessionRunnerModel.fromCatalogModel( ModelV2.Info.make({ ...model({ type: "aisdk", package: "@ai-sdk/openai", url: "https://openai.example/v1" }), - request: { headers: {}, body: {} }, + request: { settings: {}, headers: {}, body: {} }, }), Credential.Key.make({ type: "key", key: "secret" }), ) @@ -275,7 +282,7 @@ describe("SessionRunnerModel", () => { const resolved = yield* SessionRunnerModel.fromCatalogModel( ModelV2.Info.make({ ...model({ type: "aisdk", package: "@ai-sdk/openai", url: "https://openai.example/v1" }), - request: { headers: {}, body: { apiKey: "configured-secret" } }, + request: { settings: {}, headers: {}, body: { apiKey: "configured-secret" } }, }), credential, ) @@ -297,7 +304,7 @@ describe("SessionRunnerModel", () => { const resolved = yield* SessionRunnerModel.fromCatalogModel( ModelV2.Info.make({ ...model({ type: "aisdk", package: "@ai-sdk/openai", url: "https://openai.example/v1" }), - request: { headers: {}, body: {} }, + request: { settings: {}, headers: {}, body: {} }, }), Credential.OAuth.make({ type: "oauth", diff --git a/packages/llm/src/protocols/anthropic-messages.ts b/packages/llm/src/protocols/anthropic-messages.ts index 1c0dcd32a4..f20ae25c2c 100644 --- a/packages/llm/src/protocols/anthropic-messages.ts +++ b/packages/llm/src/protocols/anthropic-messages.ts @@ -148,9 +148,22 @@ const AnthropicToolChoice = Schema.Union([ Schema.Struct({ type: Schema.tag("tool"), name: Schema.String }), ]) -const AnthropicThinking = Schema.Struct({ - type: Schema.tag("enabled"), - budget_tokens: Schema.Number, +const AnthropicThinking = Schema.Union([ + Schema.Struct({ + type: Schema.tag("enabled"), + budget_tokens: Schema.Number, + }), + Schema.Struct({ + type: Schema.tag("adaptive"), + display: Schema.optional(Schema.Literals(["summarized", "omitted"])), + }), + Schema.Struct({ + type: Schema.tag("disabled"), + }), +]) + +const AnthropicOutputConfig = Schema.Struct({ + effort: Schema.optional(Schema.String), }) const AnthropicBodyFields = { @@ -166,6 +179,7 @@ const AnthropicBodyFields = { top_k: Schema.optional(Schema.Number), stop_sequences: optionalArray(Schema.String), thinking: Schema.optional(AnthropicThinking), + output_config: Schema.optional(AnthropicOutputConfig), } const AnthropicMessagesBody = Schema.Struct(AnthropicBodyFields) export type AnthropicMessagesBody = Schema.Schema.Type @@ -492,7 +506,16 @@ const anthropicOptions = (request: LLMRequest) => request.providerOptions?.anthr const lowerThinking = Effect.fn("AnthropicMessages.lowerThinking")(function* (request: LLMRequest) { const thinking = anthropicOptions(request)?.thinking - if (!ProviderShared.isRecord(thinking) || thinking.type !== "enabled") return undefined + if (!ProviderShared.isRecord(thinking)) return undefined + if (thinking.type === "adaptive") { + const display = thinking.display + return { + type: "adaptive" as const, + ...(display === "summarized" || display === "omitted" ? { display } : {}), + } + } + if (thinking.type === "disabled") return { type: "disabled" as const } + if (thinking.type !== "enabled") return undefined const budget = typeof thinking.budgetTokens === "number" ? thinking.budgetTokens @@ -503,6 +526,11 @@ const lowerThinking = Effect.fn("AnthropicMessages.lowerThinking")(function* (re return { type: "enabled" as const, budget_tokens: budget } }) +const outputConfig = (request: LLMRequest) => { + const effort = anthropicOptions(request)?.effort + return typeof effort === "string" ? { effort } : undefined +} + const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (request: LLMRequest) { const toolChoice = request.toolChoice ? yield* lowerToolChoice(request.toolChoice) : undefined const generation = request.generation @@ -549,6 +577,7 @@ const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (reques top_k: generation?.topK, stop_sequences: generation?.stop, thinking: yield* lowerThinking(request), + output_config: outputConfig(request), } }) diff --git a/packages/llm/src/protocols/openai-chat.ts b/packages/llm/src/protocols/openai-chat.ts index 9ac85b07b1..656f8179d6 100644 --- a/packages/llm/src/protocols/openai-chat.ts +++ b/packages/llm/src/protocols/openai-chat.ts @@ -168,8 +168,6 @@ interface ParserState { readonly lifecycle: Lifecycle.State } -const invalid = ProviderShared.invalidRequest - // ============================================================================= // Request Lowering // ============================================================================= @@ -333,8 +331,6 @@ const lowerMessages = Effect.fn("OpenAIChat.lowerMessages")(function* (request: const lowerOptions = Effect.fn("OpenAIChat.lowerOptions")(function* (request: LLMRequest) { const store = OpenAIOptions.store(request) const reasoningEffort = OpenAIOptions.reasoningEffort(request) - if (reasoningEffort && !OpenAIOptions.isReasoningEffort(reasoningEffort)) - return yield* invalid(`OpenAI Chat does not support reasoning effort ${reasoningEffort}`) return { ...(store !== undefined ? { store } : {}), ...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}), diff --git a/packages/llm/src/protocols/openai-responses.ts b/packages/llm/src/protocols/openai-responses.ts index 4936d31c92..de18bf42a0 100644 --- a/packages/llm/src/protocols/openai-responses.ts +++ b/packages/llm/src/protocols/openai-responses.ts @@ -457,8 +457,6 @@ const lowerOptions = Effect.fn("OpenAIResponses.lowerOptions")(function* (reques const store = OpenAIOptions.store(request) const promptCacheKey = OpenAIOptions.promptCacheKey(request) const effort = OpenAIOptions.reasoningEffort(request) - if (effort && !OpenAIOptions.isReasoningEffort(effort)) - return yield* invalid(`OpenAI Responses does not support reasoning effort ${effort}`) const summary = OpenAIOptions.reasoningSummary(request) const include = OpenAIOptions.include(request) const verbosity = OpenAIOptions.textVerbosity(request) diff --git a/packages/llm/src/protocols/utils/openai-options.ts b/packages/llm/src/protocols/utils/openai-options.ts index 51e56ae216..5414923eda 100644 --- a/packages/llm/src/protocols/utils/openai-options.ts +++ b/packages/llm/src/protocols/utils/openai-options.ts @@ -1,11 +1,9 @@ import { Schema } from "effect" -import type { LLMRequest, ReasoningEffort, TextVerbosity as TextVerbosityValue } from "../../schema" +import type { LLMRequest, TextVerbosity as TextVerbosityValue } from "../../schema" import { ReasoningEfforts, TextVerbosity } from "../../schema" -export const OpenAIReasoningEfforts = ReasoningEfforts.filter( - (effort): effort is Exclude => effort !== "max", -) -export type OpenAIReasoningEffort = (typeof OpenAIReasoningEfforts)[number] +export const OpenAIReasoningEfforts = ReasoningEfforts +export type OpenAIReasoningEffort = string // Mirrors OpenAI's `ResponseIncludable` union from the official SDK. Keep this // in lockstep with `openai-node/src/resources/responses/responses.ts`. @@ -23,22 +21,16 @@ export type OpenAIResponseIncludable = (typeof OpenAIResponseIncludables)[number export const OpenAIServiceTiers = ["auto", "default", "flex", "priority"] as const export type OpenAIServiceTier = (typeof OpenAIServiceTiers)[number] -const REASONING_EFFORTS = new Set(ReasoningEfforts) -const OPENAI_REASONING_EFFORTS = new Set(OpenAIReasoningEfforts) const TEXT_VERBOSITY = new Set(["low", "medium", "high"]) const INCLUDABLES = new Set(OpenAIResponseIncludables) const SERVICE_TIERS = new Set(OpenAIServiceTiers) -export const OpenAIReasoningEffort = Schema.Literals(OpenAIReasoningEfforts) +export const OpenAIReasoningEffort = Schema.String export const OpenAITextVerbosity = TextVerbosity export const OpenAIResponseIncludable = Schema.Literals(OpenAIResponseIncludables) export const OpenAIServiceTier = Schema.Literals(OpenAIServiceTiers) -const isAnyReasoningEffort = (effort: unknown): effort is ReasoningEffort => - typeof effort === "string" && REASONING_EFFORTS.has(effort) - -export const isReasoningEffort = (effort: unknown): effort is OpenAIReasoningEffort => - typeof effort === "string" && OPENAI_REASONING_EFFORTS.has(effort) +export const isReasoningEffort = (effort: unknown): effort is OpenAIReasoningEffort => typeof effort === "string" const isTextVerbosity = (value: unknown): value is TextVerbosityValue => typeof value === "string" && TEXT_VERBOSITY.has(value) @@ -50,9 +42,9 @@ export const store = (request: LLMRequest): boolean | undefined => { return typeof value === "boolean" ? value : undefined } -export const reasoningEffort = (request: LLMRequest): ReasoningEffort | undefined => { +export const reasoningEffort = (request: LLMRequest): string | undefined => { const value = options(request)?.reasoningEffort - return isAnyReasoningEffort(value) ? value : undefined + return typeof value === "string" ? value : undefined } export const reasoningSummary = (request: LLMRequest): "auto" | undefined => diff --git a/packages/llm/src/schema/ids.ts b/packages/llm/src/schema/ids.ts index 7eb7409802..279a3097e1 100644 --- a/packages/llm/src/schema/ids.ts +++ b/packages/llm/src/schema/ids.ts @@ -27,7 +27,7 @@ export const ToolCallID = Schema.String export type ToolCallID = Schema.Schema.Type export const ReasoningEfforts = ["none", "minimal", "low", "medium", "high", "xhigh", "max"] as const -export const ReasoningEffort = Schema.Literals(ReasoningEfforts) +export const ReasoningEffort = Schema.String export type ReasoningEffort = Schema.Schema.Type export const TextVerbosity = Schema.Literals(["low", "medium", "high"]) diff --git a/packages/llm/test/provider/anthropic-messages.test.ts b/packages/llm/test/provider/anthropic-messages.test.ts index 8989312958..14a35a4fa7 100644 --- a/packages/llm/test/provider/anthropic-messages.test.ts +++ b/packages/llm/test/provider/anthropic-messages.test.ts @@ -57,6 +57,23 @@ describe("Anthropic Messages route", () => { }), ) + it.effect("lowers adaptive thinking settings with effort", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.updateRequest(request, { + providerOptions: { + anthropic: { thinking: { type: "adaptive", display: "summarized" }, effort: "low" }, + }, + }), + ) + + expect(prepared.body).toMatchObject({ + thinking: { type: "adaptive", display: "summarized" }, + output_config: { effort: "low" }, + }) + }), + ) + it.effect("lowers chronological system updates natively for Claude Opus 4.8 with cache hints", () => Effect.gen(function* () { const prepared = yield* LLMClient.prepare( diff --git a/packages/llm/test/provider/openai-chat.test.ts b/packages/llm/test/provider/openai-chat.test.ts index b736dc9dd3..63ae09bdd8 100644 --- a/packages/llm/test/provider/openai-chat.test.ts +++ b/packages/llm/test/provider/openai-chat.test.ts @@ -98,12 +98,26 @@ describe("OpenAI Chat route", () => { LLM.request({ model: OpenAI.configure({ baseURL: "https://api.openai.test/v1/", apiKey: "test" }).chat("gpt-4o-mini"), prompt: "think", - providerOptions: { openai: { reasoningEffort: "low" } }, + providerOptions: { openai: { reasoningEffort: "max" } }, }), ) expect(prepared.body.store).toBe(false) - expect(prepared.body.reasoning_effort).toBe("low") + expect(prepared.body.reasoning_effort).toBe("max") + }), + ) + + it.effect("passes through custom OpenAI-compatible reasoning effort strings", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.request({ + model, + prompt: "think", + providerOptions: { openai: { reasoningEffort: "experimental" } }, + }), + ) + + expect(prepared.body.reasoning_effort).toBe("experimental") }), ) diff --git a/packages/llm/test/provider/openai-responses.test.ts b/packages/llm/test/provider/openai-responses.test.ts index cd8bad51af..fbc5f2a864 100644 --- a/packages/llm/test/provider/openai-responses.test.ts +++ b/packages/llm/test/provider/openai-responses.test.ts @@ -69,6 +69,16 @@ describe("OpenAI Responses route", () => { }), ) + it.effect("passes through custom OpenAI reasoning effort strings", () => + Effect.gen(function* () { + const prepared = yield* LLMClient.prepare( + LLM.updateRequest(request, { providerOptions: { openai: { reasoningEffort: "experimental" } } }), + ) + + expect(prepared.body.reasoning).toEqual({ effort: "experimental" }) + }), + ) + it.effect("omits unsupported semantic service tiers", () => Effect.gen(function* () { const prepared = yield* LLMClient.prepare( diff --git a/packages/schema/src/agent.ts b/packages/schema/src/agent.ts index f10cba2941..3d8068b89a 100644 --- a/packages/schema/src/agent.ts +++ b/packages/schema/src/agent.ts @@ -38,7 +38,7 @@ export const Info = Schema.Struct({ empty: (id: ID) => schema.make({ id, - request: { headers: {}, body: {} }, + request: { settings: {}, headers: {}, body: {} }, mode: "all", hidden: false, permissions: [ diff --git a/packages/schema/src/model.ts b/packages/schema/src/model.ts index b4c1337be1..19276b6d00 100644 --- a/packages/schema/src/model.ts +++ b/packages/schema/src/model.ts @@ -94,7 +94,7 @@ export const Info = Schema.Struct({ name: modelID, api: { id: modelID, type: "native", settings: {} }, capabilities: { tools: false, input: [], output: [] }, - request: { headers: {}, body: {} }, + request: { settings: {}, headers: {}, body: {} }, variants: [], time: { released: 0 }, cost: [], diff --git a/packages/schema/src/provider.ts b/packages/schema/src/provider.ts index 2369d8a08e..daa53f8430 100644 --- a/packages/schema/src/provider.ts +++ b/packages/schema/src/provider.ts @@ -1,6 +1,6 @@ export * as Provider from "./provider.js" -import { Schema } from "effect" +import { Effect, Schema } from "effect" import { optional } from "./schema.js" import { Integration } from "./integration.js" import { statics } from "./schema.js" @@ -43,8 +43,12 @@ export const Api = Schema.Union([AISDK, Native]) .annotate({ identifier: "Provider.Api" }) export type Api = typeof Api.Type +export const Settings = Schema.Record(Schema.String, Schema.Unknown).annotate({ identifier: "Provider.Settings" }) +export type Settings = typeof Settings.Type + export interface Request extends Schema.Schema.Type {} export const Request = Schema.Struct({ + settings: Settings.pipe(Schema.withConstructorDefault(Effect.succeed({}))), headers: Schema.Record(Schema.String, Schema.String), body: Schema.Record(Schema.String, Schema.Json), }).annotate({ identifier: "Provider.Request" }) @@ -66,7 +70,7 @@ export const Info = Schema.Struct({ id, name: id, api: { type: "native", settings: {} }, - request: { headers: {}, body: {} }, + request: { settings: {}, headers: {}, body: {} }, }), })), ) diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 145ae005b3..1a82fca91c 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -399,6 +399,8 @@ import type { V2SessionSwitchAgentResponses, V2SessionSwitchModelErrors, V2SessionSwitchModelResponses, + V2SessionSyntheticErrors, + V2SessionSyntheticResponses, V2SessionWaitErrors, V2SessionWaitResponses, V2ShellCreateErrors, @@ -5816,6 +5818,47 @@ export class Session3 extends HeyApiClient { }) } + /** + * Add synthetic message + * + * Append a synthetic message to a session and resume execution. + */ + public synthetic( + parameters: { + sessionID: string + text?: string + description?: string + metadata?: { + [key: string]: unknown + } + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "path", key: "sessionID" }, + { in: "body", key: "text" }, + { in: "body", key: "description" }, + { in: "body", key: "metadata" }, + ], + }, + ], + ) + return (options?.client ?? this.client).post({ + url: "/api/session/{sessionID}/synthetic", + ...options, + ...params, + headers: { + "Content-Type": "application/json", + ...options?.headers, + ...params.headers, + }, + }) + } + /** * Compact session * diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 55eac01f48..82fe581d70 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -942,6 +942,9 @@ export type GlobalEvent = { messageID: string text: string description?: string + metadata?: { + [key: string]: unknown + } } } | { @@ -3618,6 +3621,9 @@ export type SyncEventSessionNextSynthetic = { messageID: string text: string description?: string + metadata?: { + [key: string]: unknown + } } } } @@ -4091,7 +4097,12 @@ export type LocationInfo = { } } +export type ProviderSettings = { + [key: string]: unknown +} + export type ProviderRequest = { + settings: ProviderSettings headers: { [key: string]: string } @@ -4583,6 +4594,9 @@ export type SessionNextSynthetic = { messageID: string text: string description?: string + metadata?: { + [key: string]: unknown + } } } @@ -5120,6 +5134,7 @@ export type ModelV2Info = { api: ModelApi capabilities: ModelCapabilities request: { + settings: ProviderSettings headers: { [key: string]: string } @@ -5130,6 +5145,7 @@ export type ModelV2Info = { } variants: Array<{ id: string + settings: ProviderSettings headers: { [key: string]: string } @@ -6806,6 +6822,9 @@ export type EventSessionNextSynthetic = { messageID: string text: string description?: string + metadata?: { + [key: string]: unknown + } } } @@ -12284,6 +12303,47 @@ export type V2SessionSkillResponses = { export type V2SessionSkillResponse = V2SessionSkillResponses[keyof V2SessionSkillResponses] +export type V2SessionSyntheticData = { + body: { + text: string + description?: string + metadata?: { + [key: string]: unknown + } + } + path: { + sessionID: string + } + query?: never + url: "/api/session/{sessionID}/synthetic" +} + +export type V2SessionSyntheticErrors = { + /** + * InvalidRequestError + */ + 400: InvalidRequestError + /** + * UnauthorizedError + */ + 401: UnauthorizedError + /** + * SessionNotFoundError + */ + 404: SessionNotFoundError +} + +export type V2SessionSyntheticError = V2SessionSyntheticErrors[keyof V2SessionSyntheticErrors] + +export type V2SessionSyntheticResponses = { + /** + * + */ + 204: void +} + +export type V2SessionSyntheticResponse = V2SessionSyntheticResponses[keyof V2SessionSyntheticResponses] + export type V2SessionCompactData = { body?: never path: {