refactor(opencode): pass prepared request to native runtime

This commit is contained in:
Kit Langton 2026-05-20 21:55:45 -04:00
commit 1ecf27f389
4 changed files with 29 additions and 43 deletions

View file

@ -220,17 +220,8 @@ const live: Layer.Layer<
provider: item,
auth: info,
llmClient,
isOpenaiOauth: prepared.isOpenaiOauth,
system: prepared.system,
messages: prepared.messages,
tools: prepared.tools,
request: prepared,
toolChoice: input.toolChoice,
temperature: prepared.params.temperature,
topP: prepared.params.topP,
topK: prepared.params.topK,
maxOutputTokens: prepared.params.maxOutputTokens,
providerOptions: prepared.params.options,
headers: prepared.headers,
abort: input.abort,
})
if (native.type === "supported") {

View file

@ -12,26 +12,25 @@ import {
import type { ModelMessage } from "ai"
import type { Provider } from "@/provider/provider"
import { isRecord } from "@/util/record"
import type { Prepared as PreparedRequest } from "./request"
type ToolInput = {
readonly description?: string
readonly inputSchema?: unknown
}
export type RequestInput = {
type GenerationInput = Omit<PreparedRequest["params"], "options">
export type RequestInput = GenerationInput & {
readonly model: Provider.Model
readonly apiKey?: string
readonly baseURL?: string
readonly system?: readonly string[]
readonly messages: readonly ModelMessage[]
readonly system?: PreparedRequest["system"]
readonly messages: PreparedRequest["messages"]
readonly tools?: Record<string, ToolInput>
readonly toolChoice?: "auto" | "required" | "none"
readonly temperature?: number
readonly topP?: number
readonly topK?: number
readonly maxOutputTokens?: number
readonly providerOptions?: LLMRequest["providerOptions"]
readonly headers?: Record<string, string>
readonly headers?: PreparedRequest["headers"]
}
const providerMetadata = (value: unknown): ProviderMetadata | undefined => {

View file

@ -3,12 +3,13 @@ import type { Provider } from "@/provider/provider"
import { ProviderTransform } from "@/provider/transform"
import { errorMessage } from "@/util/error"
import { isRecord } from "@/util/record"
import { asSchema, type ModelMessage, type Tool } from "ai"
import { asSchema, type Tool } from "ai"
import { Effect } from "effect"
import * as Stream from "effect/Stream"
import { tool as nativeTool, ToolFailure, type JsonSchema, type LLMEvent } from "@opencode-ai/llm"
import type { LLMClientShape } from "@opencode-ai/llm/route"
import { LLMNative } from "./native-request"
import type { Prepared as PreparedRequest } from "./request"
export type RuntimeStatus =
| { readonly type: "supported"; readonly apiKey: string; readonly baseURL?: string }
@ -22,17 +23,13 @@ type StreamInput = {
readonly provider: Provider.Info
readonly auth: Auth.Info | undefined
readonly llmClient: LLMClientShape
readonly isOpenaiOauth: boolean
readonly system: string[]
readonly messages: ModelMessage[]
readonly tools: Record<string, Tool>
readonly request: PreparedRequest
readonly toolChoice?: "auto" | "required" | "none"
readonly temperature?: number
readonly topP?: number
readonly topK?: number
readonly maxOutputTokens?: number
readonly providerOptions?: Record<string, any>
readonly headers: Record<string, string>
readonly abort: AbortSignal
}
type ToolContext = {
readonly messages: PreparedRequest["messages"]
readonly abort: AbortSignal
}
@ -68,17 +65,18 @@ export function stream(input: StreamInput): StreamResult {
model: input.model,
apiKey: current.apiKey,
baseURL: current.baseURL,
system: input.isOpenaiOauth ? input.system : [],
messages: ProviderTransform.message(input.messages, input.model, input.providerOptions ?? {}),
system: [],
messages: ProviderTransform.message(input.request.messages, input.model, input.request.params.options),
tools: input.request.tools,
toolChoice: input.toolChoice,
temperature: input.temperature,
topP: input.topP,
topK: input.topK,
maxOutputTokens: input.maxOutputTokens,
providerOptions: ProviderTransform.providerOptions(input.model, input.providerOptions ?? {}),
headers: { ...providerHeaders(input.provider.options.headers), ...input.headers },
temperature: input.request.params.temperature,
topP: input.request.params.topP,
topK: input.request.params.topK,
maxOutputTokens: input.request.params.maxOutputTokens,
providerOptions: ProviderTransform.providerOptions(input.model, input.request.params.options),
headers: { ...providerHeaders(input.provider.options.headers), ...input.request.headers },
}),
tools: nativeTools(input.tools, input),
tools: nativeTools(input.request.tools, { messages: input.request.messages, abort: input.abort }),
}),
}
}
@ -97,7 +95,7 @@ function nativeSchema(value: unknown): JsonSchema {
return asSchema(value as Parameters<typeof asSchema>[0]).jsonSchema as JsonSchema
}
export function nativeTools(tools: Record<string, Tool>, input: Pick<StreamInput, "messages" | "abort">) {
export function nativeTools(tools: Record<string, Tool>, input: ToolContext) {
return Object.fromEntries(
Object.entries(tools).map(([name, item]) => [
name,

View file

@ -34,7 +34,6 @@ type PrepareInput = {
}
export type Prepared = {
readonly isOpenaiOauth: boolean
readonly system: string[]
readonly messages: ModelMessage[]
readonly tools: Record<string, Tool>
@ -123,7 +122,7 @@ export const prepare = Effect.fn("LLMRequestPrep.prepare")(function* (input: Pre
},
)
const { headers } = yield* input.plugin.trigger(
const { headers: pluginHeaders } = yield* input.plugin.trigger(
"chat.headers",
{
sessionID: input.sessionID,
@ -161,7 +160,6 @@ export const prepare = Effect.fn("LLMRequestPrep.prepare")(function* (input: Pre
: undefined
return {
isOpenaiOauth,
system,
messages,
tools: Object.fromEntries(Object.entries(tools).toSorted(([a], [b]) => a.localeCompare(b))),
@ -182,7 +180,7 @@ export const prepare = Effect.fn("LLMRequestPrep.prepare")(function* (input: Pre
"User-Agent": USER_AGENT,
}),
...input.model.headers,
...headers,
...pluginHeaders,
},
}
})