fix(core): omit historical images for text models
This commit is contained in:
@@ -161,7 +161,7 @@ describe("projectSlugFromPath()", () => {
|
||||
})
|
||||
|
||||
describe("text-only image handling", () => {
|
||||
it("rejects image content for models without image support", () => {
|
||||
it("rejects direct image input for models without image support", () => {
|
||||
assert.throws(
|
||||
() =>
|
||||
assertTextOnlyMessages([
|
||||
@@ -172,16 +172,17 @@ describe("text-only image handling", () => {
|
||||
]),
|
||||
/does not support image content/i,
|
||||
)
|
||||
assert.throws(
|
||||
() =>
|
||||
assertTextOnlyMessages([
|
||||
{
|
||||
role: "toolResult",
|
||||
toolCallId: "c1",
|
||||
content: [{ type: "image", data: "base64-data", mimeType: "image/png" }],
|
||||
},
|
||||
]),
|
||||
/does not support image content/i,
|
||||
})
|
||||
|
||||
it("allows historical tool-result images to be omitted for text-only models", () => {
|
||||
assert.doesNotThrow(() =>
|
||||
assertTextOnlyMessages([
|
||||
{
|
||||
role: "toolResult",
|
||||
toolCallId: "c1",
|
||||
content: [{ type: "image", data: "base64-data", mimeType: "image/png" }],
|
||||
},
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -597,6 +598,48 @@ describe("messagesToCC()", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("omits tool-result images for text-only models while preserving their text", () => {
|
||||
const result = messagesToCC([
|
||||
{ role: "user", content: "read image" },
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "toolCall", id: "c1", name: "read", arguments: {} }],
|
||||
},
|
||||
{
|
||||
role: "toolResult",
|
||||
toolCallId: "c1",
|
||||
toolName: "read",
|
||||
content: [
|
||||
{ type: "text", text: "image attached" },
|
||||
{ type: "image", data: "aGVsbG8=", mimeType: "image/jpeg" },
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
assert.equal(objectAt(result, ["2", "content", "0", "output", "value"]), "image attached")
|
||||
assert.equal(objectAt(result, ["3"]), undefined)
|
||||
})
|
||||
|
||||
it("describes an omitted image-only tool result for text-only models", () => {
|
||||
const result = messagesToCC([
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "toolCall", id: "c1", name: "read", arguments: {} }],
|
||||
},
|
||||
{
|
||||
role: "toolResult",
|
||||
toolCallId: "c1",
|
||||
toolName: "read",
|
||||
content: [{ type: "image", data: "aGVsbG8=", mimeType: "image/jpeg" }],
|
||||
},
|
||||
])
|
||||
|
||||
assert.equal(
|
||||
objectAt(result, ["1", "content", "0", "output", "value"]),
|
||||
"[Image omitted: model does not support images]",
|
||||
)
|
||||
})
|
||||
|
||||
it("preserves tool-result images as a following user image message", () => {
|
||||
const result = messagesToCC(
|
||||
[
|
||||
|
||||
+22
-8
@@ -240,12 +240,16 @@ describe("streamCommandCode — successful streams", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("rejects a tool-result image before network access for text-only models", async () => {
|
||||
it("omits a historical tool-result image after switching to a text-only model", async () => {
|
||||
server.mockResponse({
|
||||
type: "success",
|
||||
events: [JSON.stringify({ type: "finish", finishReason: "stop" })],
|
||||
})
|
||||
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
|
||||
|
||||
const events = await collectEvents(
|
||||
streamCommandCode(
|
||||
makeModel({ id: "deepseek/deepseek-v4-pro" }),
|
||||
makeModel({ id: "deepseek/deepseek-v4-flash" }),
|
||||
makeContext({
|
||||
messages: [
|
||||
{ role: "user", content: "read the image" },
|
||||
@@ -257,19 +261,29 @@ describe("streamCommandCode — successful streams", () => {
|
||||
role: "toolResult",
|
||||
toolCallId: "c1",
|
||||
toolName: "read",
|
||||
content: [{ type: "image", data: "aGVsbG8=", mimeType: "image/png" }],
|
||||
content: [
|
||||
{ type: "text", text: "image attached" },
|
||||
{ type: "image", data: "aGVsbG8=", mimeType: "image/png" },
|
||||
],
|
||||
},
|
||||
{ role: "user", content: "continue without the image" },
|
||||
],
|
||||
}),
|
||||
{ apiKey: "mock-key" },
|
||||
),
|
||||
)
|
||||
|
||||
const lastEvent = events.at(-1)
|
||||
assert.equal(lastEvent?.type, "error")
|
||||
if (lastEvent?.type !== "error") throw new Error("expected error event")
|
||||
assert.match(lastEvent.error.errorMessage ?? "", /does not support image content/i)
|
||||
assert.equal(server.requestCount(), 0)
|
||||
assert.equal(events.at(-1)?.type, "done")
|
||||
assert.equal(server.requestCount(), 1)
|
||||
const body = server.lastRequestBody()
|
||||
assert.equal(
|
||||
objectAt(body, ["params", "messages", "2", "content", "0", "output", "value"]),
|
||||
"image attached",
|
||||
)
|
||||
assert.equal(
|
||||
objectAt(body, ["params", "messages", "3", "content"]),
|
||||
"continue without the image",
|
||||
)
|
||||
})
|
||||
|
||||
it("rejects images before network access for text-only models", async () => {
|
||||
|
||||
Reference in New Issue
Block a user