feat(opencode): convert native LLM message history
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import * as LLMCore from "@opencode-ai/llm/llm"
|
||||
import type { Message as CoreMessage } from "@opencode-ai/llm/schema"
|
||||
import type { ContentPart, Message as CoreMessage } from "@opencode-ai/llm/schema"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { ProviderLLMBridge } from "@/provider/llm-bridge"
|
||||
import { ProviderTransform } from "@/provider"
|
||||
@@ -27,6 +27,7 @@ export type RequestInput = {
|
||||
readonly system?: ReadonlyArray<string>
|
||||
readonly messages: ReadonlyArray<MessageV2.WithParts>
|
||||
readonly tools?: ReadonlyArray<Tool.Def>
|
||||
readonly toolChoice?: LLMCore.RequestInput["toolChoice"]
|
||||
readonly generation?: LLMCore.RequestInput["generation"]
|
||||
readonly metadata?: Record<string, unknown>
|
||||
readonly native?: Record<string, unknown>
|
||||
@@ -37,17 +38,84 @@ const isDefined = <T>(value: T | undefined): value is T => value !== undefined
|
||||
const textContent = (message: MessageV2.WithParts) =>
|
||||
message.parts.flatMap((part) => (part.type === "text" && !part.ignored ? [LLMCore.text(part.text)] : []))
|
||||
|
||||
const message = (input: MessageV2.WithParts): CoreMessage | undefined => {
|
||||
const providerMeta = (metadata: Record<string, unknown> | undefined) => {
|
||||
if (!metadata) return undefined
|
||||
const { providerExecuted: _, ...rest } = metadata
|
||||
return Object.keys(rest).length > 0 ? rest : undefined
|
||||
}
|
||||
|
||||
const toolResultValue = (part: MessageV2.ToolPart) => {
|
||||
if (part.state.status === "completed") {
|
||||
return {
|
||||
type: "text" as const,
|
||||
value: part.state.time.compacted ? "[Old tool result content cleared]" : part.state.output,
|
||||
}
|
||||
}
|
||||
if (part.state.status === "error") {
|
||||
const output = part.state.metadata?.interrupted === true ? part.state.metadata.output : undefined
|
||||
if (typeof output === "string") return { type: "text" as const, value: output }
|
||||
return { type: "error" as const, value: part.state.error }
|
||||
}
|
||||
return { type: "error" as const, value: "[Tool execution was interrupted]" }
|
||||
}
|
||||
|
||||
const assistantMessages = (input: MessageV2.WithParts) => {
|
||||
const content: ContentPart[] = []
|
||||
const results: CoreMessage[] = []
|
||||
for (const part of input.parts) {
|
||||
if (part.type === "text" && !part.ignored) content.push(LLMCore.text(part.text))
|
||||
if (part.type === "reasoning") content.push({ type: "reasoning", text: part.text, metadata: part.metadata })
|
||||
if (part.type === "tool") {
|
||||
const metadata = providerMeta(part.metadata)
|
||||
content.push(
|
||||
LLMCore.toolCall({
|
||||
id: part.callID,
|
||||
name: part.tool,
|
||||
input: part.state.input,
|
||||
providerExecuted: part.metadata?.providerExecuted === true ? true : undefined,
|
||||
metadata,
|
||||
}),
|
||||
)
|
||||
results.push(
|
||||
LLMCore.toolMessage({
|
||||
id: part.callID,
|
||||
name: part.tool,
|
||||
result: toolResultValue(part),
|
||||
providerExecuted: part.metadata?.providerExecuted === true ? true : undefined,
|
||||
metadata,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
content.length === 0
|
||||
? undefined
|
||||
: LLMCore.message({
|
||||
id: input.info.id,
|
||||
role: "assistant",
|
||||
content,
|
||||
native: {
|
||||
opencodeMessageID: input.info.id,
|
||||
},
|
||||
}),
|
||||
...results,
|
||||
].filter(isDefined)
|
||||
}
|
||||
|
||||
const message = (input: MessageV2.WithParts): ReadonlyArray<CoreMessage> => {
|
||||
if (input.info.role === "assistant") return assistantMessages(input)
|
||||
|
||||
const content = textContent(input)
|
||||
if (content.length === 0) return undefined
|
||||
return LLMCore.message({
|
||||
if (content.length === 0) return []
|
||||
return [LLMCore.message({
|
||||
id: input.info.id,
|
||||
role: input.info.role,
|
||||
content,
|
||||
native: {
|
||||
opencodeMessageID: input.info.id,
|
||||
},
|
||||
})
|
||||
})]
|
||||
}
|
||||
|
||||
export const toolDefinition = (input: { readonly model: Provider.Model; readonly tool: Tool.Def }) =>
|
||||
@@ -75,8 +143,9 @@ export const request = Effect.fn("LLMNative.request")(function* (input: RequestI
|
||||
id: input.id,
|
||||
model,
|
||||
system: input.system?.filter((part) => part.trim() !== "").map(LLMCore.system) ?? [],
|
||||
messages: input.messages.map(message).filter(isDefined),
|
||||
messages: input.messages.flatMap(message),
|
||||
tools: input.tools?.map((tool) => toolDefinition({ model: input.model, tool })) ?? [],
|
||||
toolChoice: input.toolChoice,
|
||||
generation: input.generation,
|
||||
metadata: input.metadata,
|
||||
native: {
|
||||
|
||||
Reference in New Issue
Block a user