Skip to content

Commit de2de09

Browse files
committed
fix: rm user message when dealing w/ image attachments, use proper tool attachment instead
1 parent 0233dd1 commit de2de09

File tree

2 files changed

+41
-29
lines changed

2 files changed

+41
-29
lines changed

packages/opencode/src/session/message-v2.ts

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import { BusEvent } from "@/bus/bus-event"
22
import z from "zod"
33
import { NamedError } from "@opencode-ai/util/error"
4-
import { APICallError, convertToModelMessages, LoadAPIKeyError, type ModelMessage, type UIMessage } from "ai"
4+
import {
5+
APICallError,
6+
convertToModelMessages,
7+
LoadAPIKeyError,
8+
type ModelMessage,
9+
type UIMessage,
10+
type ToolSet,
11+
} from "ai"
512
import { Identifier } from "../id/id"
613
import { LSP } from "../lsp"
714
import { Snapshot } from "@/snapshot"
@@ -432,7 +439,7 @@ export namespace MessageV2 {
432439
})
433440
export type WithParts = z.infer<typeof WithParts>
434441

435-
export function toModelMessage(input: WithParts[]): ModelMessage[] {
442+
export function toModelMessage(input: WithParts[], options?: { tools?: ToolSet }): ModelMessage[] {
436443
const result: UIMessage[] = []
437444

438445
for (const msg of input) {
@@ -503,30 +510,14 @@ export namespace MessageV2 {
503510
})
504511
if (part.type === "tool") {
505512
if (part.state.status === "completed") {
506-
if (part.state.attachments?.length) {
507-
result.push({
508-
id: Identifier.ascending("message"),
509-
role: "user",
510-
parts: [
511-
{
512-
type: "text",
513-
text: `Tool ${part.tool} returned an attachment:`,
514-
},
515-
...part.state.attachments.map((attachment) => ({
516-
type: "file" as const,
517-
url: attachment.url,
518-
mediaType: attachment.mime,
519-
filename: attachment.filename,
520-
})),
521-
],
522-
})
523-
}
524513
assistantMessage.parts.push({
525514
type: ("tool-" + part.tool) as `tool-${string}`,
526515
state: "output-available",
527516
toolCallId: part.callID,
528517
input: part.state.input,
529-
output: part.state.time.compacted ? "[Old tool result content cleared]" : part.state.output,
518+
output: part.state.time.compacted
519+
? "[Old tool result content cleared]"
520+
: { output: part.state.output, attachments: part.state.attachments },
530521
callProviderMetadata: part.metadata,
531522
})
532523
}
@@ -565,7 +556,12 @@ export namespace MessageV2 {
565556
}
566557
}
567558

568-
return convertToModelMessages(result.filter((msg) => msg.parts.some((part) => part.type !== "step-start")))
559+
return convertToModelMessages(
560+
result.filter((msg) => msg.parts.some((part) => part.type !== "step-start")),
561+
{
562+
tools: options?.tools,
563+
},
564+
)
569565
}
570566

571567
export const stream = fn(Identifier.schema("session"), async function* (sessionID) {

packages/opencode/src/session/prompt.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ export namespace SessionPrompt {
597597
sessionID,
598598
system: [...(await SystemPrompt.environment()), ...(await SystemPrompt.custom())],
599599
messages: [
600-
...MessageV2.toModelMessage(sessionMessages),
600+
...MessageV2.toModelMessage(sessionMessages, { tools }),
601601
...(isLastStep
602602
? [
603603
{
@@ -716,10 +716,18 @@ export namespace SessionPrompt {
716716
)
717717
return result
718718
},
719-
toModelOutput(result) {
719+
toModelOutput(result: { output: string; attachments?: MessageV2.FilePart[] }) {
720+
if (!result.attachments?.length) return { type: "text", value: result.output }
720721
return {
721-
type: "text",
722-
value: result.output,
722+
type: "content",
723+
value: [
724+
{ type: "text", text: result.output },
725+
...result.attachments.map((a) => ({
726+
type: "media" as const,
727+
data: a.url.slice(a.url.indexOf(",") + 1),
728+
mediaType: a.mime,
729+
})),
730+
],
723731
}
724732
},
725733
})
@@ -806,10 +814,18 @@ export namespace SessionPrompt {
806814
content: result.content, // directly return content to preserve ordering when outputting to model
807815
}
808816
}
809-
item.toModelOutput = (result) => {
817+
item.toModelOutput = (result: { output: string; attachments?: MessageV2.FilePart[] }) => {
818+
if (!result.attachments?.length) return { type: "text", value: result.output }
810819
return {
811-
type: "text",
812-
value: result.output,
820+
type: "content",
821+
value: [
822+
{ type: "text", text: result.output },
823+
...result.attachments.map((a) => ({
824+
type: "media" as const,
825+
data: a.url.slice(a.url.indexOf(",") + 1),
826+
mediaType: a.mime,
827+
})),
828+
],
813829
}
814830
}
815831
tools[key] = item

0 commit comments

Comments
 (0)