summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorZhiming Guo <[email protected]>2026-02-05 18:54:07 +1100
committerGitHub <[email protected]>2026-02-05 01:54:07 -0600
commit72de9fe7a6993e053d302b4f11f59ae3149e5a96 (patch)
treecf03e98b3680e1b492d590101ecd7a95fae4d3e8
parentbec1148192792d438285e30b2473f41eea1af6a0 (diff)
downloadopencode-72de9fe7a6993e053d302b4f11f59ae3149e5a96.tar.gz
opencode-72de9fe7a6993e053d302b4f11f59ae3149e5a96.zip
fix(opencode): Fixes image reading with OpenAI-compatible providers like Kimi K2.5. (#11323)
Co-authored-by: Aiden Cline <[email protected]> Co-authored-by: Aiden Cline <[email protected]>
-rw-r--r--packages/opencode/src/session/message-v2.ts56
1 files changed, 54 insertions, 2 deletions
diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts
index 6358c6c5e..b6043b032 100644
--- a/packages/opencode/src/session/message-v2.ts
+++ b/packages/opencode/src/session/message-v2.ts
@@ -438,6 +438,26 @@ export namespace MessageV2 {
export function toModelMessages(input: WithParts[], model: Provider.Model): ModelMessage[] {
const result: UIMessage[] = []
const toolNames = new Set<string>()
+ // Track media from tool results that need to be injected as user messages
+ // for providers that don't support media in tool results.
+ //
+ // OpenAI-compatible APIs only support string content in tool results, so we need
+ // to extract media and inject as user messages. Other SDKs (anthropic, google,
+ // bedrock) handle type: "content" with media parts natively.
+ //
+ // Only apply this workaround if the model actually supports image input -
+ // otherwise there's no point extracting images.
+ const supportsMediaInToolResults = (() => {
+ if (model.api.npm === "@ai-sdk/anthropic") return true
+ if (model.api.npm === "@ai-sdk/openai") return true
+ if (model.api.npm === "@ai-sdk/amazon-bedrock") return true
+ if (model.api.npm === "@ai-sdk/google-vertex/anthropic") return true
+ if (model.api.npm === "@ai-sdk/google") {
+ const id = model.api.id.toLowerCase()
+ return id.includes("gemini-3") && !id.includes("gemini-2")
+ }
+ return false
+ })()
const toModelOutput = (output: unknown) => {
if (typeof output === "string") {
@@ -514,6 +534,7 @@ export namespace MessageV2 {
if (msg.info.role === "assistant") {
const differentModel = `${model.providerID}/${model.id}` !== `${msg.info.providerID}/${msg.info.modelID}`
+ const media: Array<{ mime: string; url: string }> = []
if (
msg.info.error &&
@@ -545,11 +566,23 @@ export namespace MessageV2 {
if (part.state.status === "completed") {
const outputText = part.state.time.compacted ? "[Old tool result content cleared]" : part.state.output
const attachments = part.state.time.compacted ? [] : (part.state.attachments ?? [])
+
+ // For providers that don't support media in tool results, extract media files
+ // (images, PDFs) to be sent as a separate user message
+ const isMediaAttachment = (a: { mime: string }) =>
+ a.mime.startsWith("image/") || a.mime === "application/pdf"
+ const mediaAttachments = attachments.filter(isMediaAttachment)
+ const nonMediaAttachments = attachments.filter((a) => !isMediaAttachment(a))
+ if (!supportsMediaInToolResults && mediaAttachments.length > 0) {
+ media.push(...mediaAttachments)
+ }
+ const finalAttachments = supportsMediaInToolResults ? attachments : nonMediaAttachments
+
const output =
- attachments.length > 0
+ finalAttachments.length > 0
? {
text: outputText,
- attachments,
+ attachments: finalAttachments,
}
: outputText
@@ -593,6 +626,25 @@ export namespace MessageV2 {
}
if (assistantMessage.parts.length > 0) {
result.push(assistantMessage)
+ // Inject pending media as a user message for providers that don't support
+ // media (images, PDFs) in tool results
+ if (media.length > 0) {
+ result.push({
+ id: Identifier.ascending("message"),
+ role: "user",
+ parts: [
+ {
+ type: "text" as const,
+ text: "Attached image(s) from tool result:",
+ },
+ ...media.map((attachment) => ({
+ type: "file" as const,
+ url: attachment.url,
+ mediaType: attachment.mime,
+ })),
+ ],
+ })
+ }
}
}
}