refactor(session): simplify LLM event adapter

This commit is contained in:
Kit Langton 2026-05-10 21:50:04 -04:00
commit 4481d46071
2 changed files with 35 additions and 22 deletions

View file

@ -256,9 +256,15 @@ export const LLMEvent = Object.assign(llmEventTagged, {
toolResult: (input: WithID<ToolResult, ToolCallID>) => ToolResult.make({ ...input, id: toolCallID(input.id) }),
toolError: (input: WithID<ToolError, ToolCallID>) => ToolError.make({ ...input, id: toolCallID(input.id) }),
stepFinish: (input: WithUsage<StepFinish>) =>
StepFinish.make({ ...input, usage: input.usage instanceof Usage ? input.usage : new Usage(input.usage ?? {}) }),
StepFinish.make({
...input,
usage: input.usage === undefined ? undefined : input.usage instanceof Usage ? input.usage : new Usage(input.usage),
}),
requestFinish: (input: WithUsage<RequestFinish>) =>
RequestFinish.make({ ...input, usage: input.usage instanceof Usage ? input.usage : new Usage(input.usage ?? {}) }),
RequestFinish.make({
...input,
usage: input.usage === undefined ? undefined : input.usage instanceof Usage ? input.usage : new Usage(input.usage),
}),
providerError: ProviderErrorEvent.make,
is: {
requestStart: llmEventTagged.guards["request-start"],

View file

@ -1,4 +1,4 @@
import { ContentBlockID, FinishReason, LLMEvent, ProviderMetadata, ToolCallID, ToolResultValue, Usage } from "@opencode-ai/llm"
import { FinishReason, LLMEvent, ProviderMetadata, ToolResultValue } from "@opencode-ai/llm"
import { Effect, Schema } from "effect"
import { type streamText } from "ai"
import { errorMessage } from "@/util/error"
@ -11,15 +11,12 @@ export function adapterState() {
step: 0,
text: 0,
reasoning: 0,
currentTextID: undefined as ContentBlockID | undefined,
currentReasoningID: undefined as ContentBlockID | undefined,
currentTextID: undefined as string | undefined,
currentReasoningID: undefined as string | undefined,
toolNames: {} as Record<string, string>,
}
}
const contentBlockID = (value: string) => ContentBlockID.make(value)
const toolCallID = (value: string) => ToolCallID.make(value)
function finishReason(value: string | undefined): FinishReason {
return Schema.is(FinishReason)(value) ? value : "unknown"
}
@ -28,7 +25,7 @@ function providerMetadata(value: unknown): ProviderMetadata | undefined {
return Schema.is(ProviderMetadata)(value) ? value : undefined
}
function usage(value: unknown): Usage | undefined {
function usage(value: unknown) {
if (!value || typeof value !== "object") return undefined
const item = value as {
inputTokens?: number
@ -49,7 +46,17 @@ function usage(value: unknown): Usage | undefined {
cacheWriteInputTokens: item.inputTokenDetails?.cacheWriteTokens,
}).filter((entry) => entry[1] !== undefined),
)
return new Usage(result)
return result
}
function currentTextID(state: ReturnType<typeof adapterState>, id: string | undefined) {
state.currentTextID = id ?? state.currentTextID ?? `text-${state.text++}`
return state.currentTextID
}
function currentReasoningID(state: ReturnType<typeof adapterState>, id: string | undefined) {
state.currentReasoningID = id ?? state.currentReasoningID ?? `reasoning-${state.reasoning++}`
return state.currentReasoningID
}
export function toLLMEvents(
@ -86,7 +93,7 @@ export function toLLMEvents(
case "text-start":
return Effect.sync(() => {
state.currentTextID = contentBlockID(event.id ?? `text-${state.text++}`)
state.currentTextID = currentTextID(state, event.id)
return [
LLMEvent.textStart({
id: state.currentTextID,
@ -98,7 +105,7 @@ export function toLLMEvents(
case "text-delta":
return Effect.succeed([
LLMEvent.textDelta({
id: event.id ? contentBlockID(event.id) : (state.currentTextID ?? contentBlockID(`text-${state.text++}`)),
id: currentTextID(state, event.id),
text: event.text,
}),
])
@ -106,14 +113,14 @@ export function toLLMEvents(
case "text-end":
return Effect.succeed([
LLMEvent.textEnd({
id: event.id ? contentBlockID(event.id) : (state.currentTextID ?? contentBlockID(`text-${state.text++}`)),
id: currentTextID(state, event.id),
providerMetadata: providerMetadata(event.providerMetadata),
}),
])
case "reasoning-start":
return Effect.sync(() => {
state.currentReasoningID = contentBlockID(event.id)
state.currentReasoningID = currentReasoningID(state, event.id)
return [
LLMEvent.reasoningStart({
id: state.currentReasoningID,
@ -125,14 +132,14 @@ export function toLLMEvents(
case "reasoning-delta":
return Effect.succeed([
LLMEvent.reasoningDelta({
id: event.id ? contentBlockID(event.id) : (state.currentReasoningID ?? contentBlockID(`reasoning-${state.reasoning++}`)),
id: currentReasoningID(state, event.id),
text: event.text,
}),
])
case "reasoning-end":
return Effect.sync(() => {
const id = contentBlockID(event.id)
const id = currentReasoningID(state, event.id)
state.currentReasoningID = undefined
return [
LLMEvent.reasoningEnd({
@ -147,7 +154,7 @@ export function toLLMEvents(
state.toolNames[event.id] = event.toolName
return [
LLMEvent.toolInputStart({
id: toolCallID(event.id),
id: event.id,
name: event.toolName,
providerMetadata: providerMetadata(event.providerMetadata),
}),
@ -157,7 +164,7 @@ export function toLLMEvents(
case "tool-input-delta":
return Effect.succeed([
LLMEvent.toolInputDelta({
id: toolCallID(event.id),
id: event.id,
name: state.toolNames[event.id] ?? "unknown",
text: event.delta ?? "",
}),
@ -166,7 +173,7 @@ export function toLLMEvents(
case "tool-input-end":
return Effect.succeed([
LLMEvent.toolInputEnd({
id: toolCallID(event.id),
id: event.id,
name: state.toolNames[event.id] ?? "unknown",
}),
])
@ -176,7 +183,7 @@ export function toLLMEvents(
state.toolNames[event.toolCallId] = event.toolName
return [
LLMEvent.toolCall({
id: toolCallID(event.toolCallId),
id: event.toolCallId,
name: event.toolName,
input: event.input,
providerExecuted: "providerExecuted" in event ? event.providerExecuted : undefined,
@ -191,7 +198,7 @@ export function toLLMEvents(
delete state.toolNames[event.toolCallId]
return [
LLMEvent.toolResult({
id: toolCallID(event.toolCallId),
id: event.toolCallId,
name,
result: ToolResultValue.make(event.output),
providerExecuted: "providerExecuted" in event ? event.providerExecuted : undefined,
@ -205,7 +212,7 @@ export function toLLMEvents(
delete state.toolNames[event.toolCallId]
return [
LLMEvent.toolError({
id: toolCallID(event.toolCallId),
id: event.toolCallId,
name,
message: errorMessage(event.error),
}),