refactor(core): simplify location filesystem (#31545)
This commit is contained in:
@@ -235,7 +235,7 @@ describe("Anthropic Messages route", () => {
|
||||
resultType: "content",
|
||||
result: [
|
||||
{ type: "text", text: "Image read successfully" },
|
||||
{ type: "media", mediaType: "image/png", data: "AAECAw==" },
|
||||
{ type: "file", uri: "data:image/png;base64,AAECAw==", mime: "image/png" },
|
||||
],
|
||||
}),
|
||||
],
|
||||
@@ -262,7 +262,7 @@ describe("Anthropic Messages route", () => {
|
||||
id: "call_1",
|
||||
name: "screenshot",
|
||||
resultType: "content",
|
||||
result: [{ type: "media", mediaType: "image/jpeg", data: "/9j/AA==" }],
|
||||
result: [{ type: "file", uri: "data:image/jpeg;base64,/9j/AA==", mime: "image/jpeg" }],
|
||||
}),
|
||||
],
|
||||
cache: "none",
|
||||
@@ -287,7 +287,7 @@ describe("Anthropic Messages route", () => {
|
||||
id: "call_1",
|
||||
name: "fetch",
|
||||
resultType: "content",
|
||||
result: [{ type: "media", mediaType: "audio/mpeg", data: "AAECAw==" }],
|
||||
result: [{ type: "file", uri: "data:audio/mpeg;base64,AAECAw==", mime: "audio/mpeg" }],
|
||||
}),
|
||||
],
|
||||
cache: "none",
|
||||
|
||||
@@ -189,7 +189,7 @@ describe("Bedrock Converse route", () => {
|
||||
type: "content",
|
||||
value: [
|
||||
{ type: "text", text: "Screenshot captured." },
|
||||
{ type: "media", mediaType: "image/png", data: "AAAA" },
|
||||
{ type: "file", uri: "data:image/png;base64,AAAA", mime: "image/png" },
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -124,7 +124,7 @@ describe("Gemini route", () => {
|
||||
type: "content",
|
||||
value: [
|
||||
{ type: "text", text: "Image read successfully" },
|
||||
{ type: "media", mediaType: "image/png", data: "AAECAw==", filename: "pixel.png" },
|
||||
{ type: "file", uri: "data:image/png;base64,AAECAw==", mime: "image/png", name: "pixel.png" },
|
||||
],
|
||||
},
|
||||
}),
|
||||
@@ -163,7 +163,7 @@ describe("Gemini route", () => {
|
||||
name: "read",
|
||||
result: {
|
||||
type: "content",
|
||||
value: [{ type: "media", mediaType: "image/jpeg", data: "data:image/jpeg;base64,/9j/" }],
|
||||
value: [{ type: "file", uri: "data:image/jpeg;base64,/9j/", mime: "image/jpeg" }],
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -238,7 +238,7 @@ describe("OpenAI Chat route", () => {
|
||||
type: "content",
|
||||
value: [
|
||||
{ type: "text", text: "Image read successfully" },
|
||||
{ type: "media", mediaType: "image/png", data: "AAECAw==", filename: "pixel.png" },
|
||||
{ type: "file", uri: "data:image/png;base64,AAECAw==", mime: "image/png", name: "pixel.png" },
|
||||
],
|
||||
},
|
||||
}),
|
||||
@@ -285,13 +285,19 @@ describe("OpenAI Chat route", () => {
|
||||
type: "tool-result",
|
||||
id: "call_1",
|
||||
name: "read",
|
||||
result: { type: "content", value: [{ type: "media", mediaType: "image/png", data: "AAEC" }] },
|
||||
result: {
|
||||
type: "content",
|
||||
value: [{ type: "file", uri: "data:image/png;base64,AAEC", mime: "image/png" }],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "tool-result",
|
||||
id: "call_2",
|
||||
name: "read",
|
||||
result: { type: "content", value: [{ type: "media", mediaType: "image/jpeg", data: "/9j/" }] },
|
||||
result: {
|
||||
type: "content",
|
||||
value: [{ type: "file", uri: "data:image/jpeg;base64,/9j/", mime: "image/jpeg" }],
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
@@ -321,12 +327,12 @@ describe("OpenAI Chat route", () => {
|
||||
Message.tool({
|
||||
id: "call_1",
|
||||
name: "read",
|
||||
result: { type: "content", value: [{ type: "media", mediaType: "image/png", data: "AAEC" }] },
|
||||
result: { type: "content", value: [{ type: "file", uri: "data:image/png;base64,AAEC", mime: "image/png" }] },
|
||||
}),
|
||||
Message.tool({
|
||||
id: "call_2",
|
||||
name: "read",
|
||||
result: { type: "content", value: [{ type: "media", mediaType: "image/webp", data: "UklG" }] },
|
||||
result: { type: "content", value: [{ type: "file", uri: "data:image/webp;base64,UklG", mime: "image/webp" }] },
|
||||
}),
|
||||
Message.system("Inspect both images."),
|
||||
],
|
||||
|
||||
@@ -377,7 +377,7 @@ describe("OpenAI Responses route", () => {
|
||||
resultType: "content",
|
||||
result: [
|
||||
{ type: "text", text: "Image read successfully" },
|
||||
{ type: "media", mediaType: "image/png", data: "AAECAw==" },
|
||||
{ type: "file", uri: "data:image/png;base64,AAECAw==", mime: "image/png" },
|
||||
],
|
||||
}),
|
||||
],
|
||||
@@ -403,7 +403,7 @@ describe("OpenAI Responses route", () => {
|
||||
id: "call_1",
|
||||
name: "screenshot",
|
||||
resultType: "content",
|
||||
result: [{ type: "media", mediaType: "image/png", data: "AAECAw==" }],
|
||||
result: [{ type: "file", uri: "data:image/png;base64,AAECAw==", mime: "image/png" }],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@@ -427,7 +427,7 @@ describe("OpenAI Responses route", () => {
|
||||
id: "call_1",
|
||||
name: "fetch",
|
||||
resultType: "content",
|
||||
result: [{ type: "media", mediaType: "audio/mpeg", data: "AAECAw==" }],
|
||||
result: [{ type: "file", uri: "data:audio/mpeg;base64,AAECAw==", mime: "audio/mpeg" }],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -388,7 +388,7 @@ const runImageToolResultScenario = (context: GoldenScenarioContext) =>
|
||||
resultType: "content",
|
||||
result: [
|
||||
{ type: "text", text: "Image read successfully" },
|
||||
{ type: "media", mediaType: "image/png", data: image },
|
||||
{ type: "file", uri: `data:image/png;base64,${image}`, mime: "image/png" },
|
||||
],
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
ToolChoice,
|
||||
ToolContent,
|
||||
ToolOutput,
|
||||
toolFileSourceFromUri,
|
||||
toDefinitions,
|
||||
} from "../src"
|
||||
import { Auth, LLMClient } from "../src/route"
|
||||
@@ -217,7 +216,7 @@ describe("LLMClient tools", () => {
|
||||
execute: () => Effect.succeed({ mime: "image/png", data: "AAECAw==" }),
|
||||
toStructuredOutput: (output) => ({ mime: output.mime }),
|
||||
toModelOutput: ({ output }) => [
|
||||
{ type: "file", source: { type: "data", data: output.data }, mime: output.mime },
|
||||
{ type: "file", uri: `data:${output.mime};base64,${output.data}`, mime: output.mime },
|
||||
],
|
||||
})
|
||||
|
||||
@@ -228,81 +227,80 @@ describe("LLMClient tools", () => {
|
||||
|
||||
expect(dispatched.output).toEqual({
|
||||
structured: { mime: "image/png" },
|
||||
content: [{ type: "file", source: { type: "data", data: "AAECAw==" }, mime: "image/png" }],
|
||||
content: [{ type: "file", uri: "data:image/png;base64,AAECAw==", mime: "image/png" }],
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("models canonical tool files with explicit data, url, and file sources", () =>
|
||||
it.effect("models canonical tool files with URIs", () =>
|
||||
Effect.sync(() => {
|
||||
const decode = Schema.decodeUnknownSync(ToolContent)
|
||||
|
||||
expect(decode({ type: "file", source: { type: "data", data: "AAAA" }, mime: "image/png" })).toEqual({
|
||||
expect(decode({ type: "file", uri: "data:image/png;base64,AAAA", mime: "image/png" })).toEqual({
|
||||
type: "file",
|
||||
source: { type: "data", data: "AAAA" },
|
||||
uri: "data:image/png;base64,AAAA",
|
||||
mime: "image/png",
|
||||
})
|
||||
expect(
|
||||
decode({ type: "file", source: { type: "url", url: "https://example.test/image.png" }, mime: "image/png" }),
|
||||
decode({ type: "file", uri: "https://example.test/image.png", mime: "image/png" }),
|
||||
).toEqual({
|
||||
type: "file",
|
||||
source: { type: "url", url: "https://example.test/image.png" },
|
||||
uri: "https://example.test/image.png",
|
||||
mime: "image/png",
|
||||
})
|
||||
expect(
|
||||
decode({ type: "file", source: { type: "file", uri: "file:///tmp/image.png" }, mime: "image/png" }),
|
||||
decode({ type: "file", uri: "file:///tmp/image.png", mime: "image/png" }),
|
||||
).toEqual({
|
||||
type: "file",
|
||||
source: { type: "file", uri: "file:///tmp/image.png" },
|
||||
uri: "file:///tmp/image.png",
|
||||
mime: "image/png",
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("converts canonical data files deliberately and rejects unmaterialized sources", () =>
|
||||
it.effect("preserves canonical tool file URIs", () =>
|
||||
Effect.sync(() => {
|
||||
expect(
|
||||
ToolOutput.toResultValue(
|
||||
ToolOutput.make({}, [{ type: "file", source: { type: "data", data: "AAAA" }, mime: "image/png" }]),
|
||||
),
|
||||
).toEqual({ type: "content", value: [{ type: "media", mediaType: "image/png", data: "AAAA" }] })
|
||||
expect(
|
||||
ToolOutput.toResultValue(
|
||||
ToolOutput.make({}, [
|
||||
{ type: "file", source: { type: "url", url: "https://example.test/image.png" }, mime: "image/png" },
|
||||
]),
|
||||
ToolOutput.make({}, [{ type: "file", uri: "data:image/png;base64,AAAA", mime: "image/png" }]),
|
||||
),
|
||||
).toEqual({
|
||||
type: "error",
|
||||
value: 'Tool file source "url" must be materialized to inline data before provider conversion',
|
||||
type: "content",
|
||||
value: [{ type: "file", uri: "data:image/png;base64,AAAA", mime: "image/png" }],
|
||||
})
|
||||
expect(
|
||||
ToolOutput.toResultValue(
|
||||
ToolOutput.make({}, [
|
||||
{ type: "file", source: { type: "file", uri: "file:///tmp/image.png" }, mime: "image/png" },
|
||||
{ type: "file", uri: "https://example.test/image.png", mime: "image/png" },
|
||||
]),
|
||||
),
|
||||
).toEqual({
|
||||
type: "error",
|
||||
value: 'Tool file source "file" must be materialized to inline data before provider conversion',
|
||||
type: "content",
|
||||
value: [{ type: "file", uri: "https://example.test/image.png", mime: "image/png" }],
|
||||
})
|
||||
expect(toolFileSourceFromUri("data:image/png;base64,AAAA")).toEqual({ type: "data", data: "AAAA" })
|
||||
expect(toolFileSourceFromUri("https://example.test/image.png")).toEqual({
|
||||
type: "url",
|
||||
url: "https://example.test/image.png",
|
||||
expect(
|
||||
ToolOutput.toResultValue(
|
||||
ToolOutput.make({}, [
|
||||
{ type: "file", uri: "file:///tmp/image.png", mime: "image/png" },
|
||||
]),
|
||||
),
|
||||
).toEqual({
|
||||
type: "content",
|
||||
value: [{ type: "file", uri: "file:///tmp/image.png", mime: "image/png" }],
|
||||
})
|
||||
expect(toolFileSourceFromUri("file:///tmp/image.png")).toEqual({ type: "file", uri: "file:///tmp/image.png" })
|
||||
expect(() => toolFileSourceFromUri("opaque-value")).toThrow("Unsupported tool file URI")
|
||||
expect(() =>
|
||||
expect(
|
||||
ToolOutput.fromResultValue({
|
||||
type: "content",
|
||||
value: [{ type: "media", mediaType: "image/png", data: "https://example.test/image.png" }],
|
||||
value: [{ type: "file", uri: "https://example.test/image.png", mime: "image/png" }],
|
||||
}),
|
||||
).toThrow("Legacy tool-result media must contain raw base64 bytes or a base64 data URI")
|
||||
).toEqual({
|
||||
structured: {},
|
||||
content: [{ type: "file", uri: "https://example.test/image.png", mime: "image/png" }],
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("settles projected url files as materialization errors", () =>
|
||||
it.effect("settles projected URL files as canonical tool results", () =>
|
||||
Effect.gen(function* () {
|
||||
const remote = Tool.make({
|
||||
description: "Return a remote file.",
|
||||
@@ -310,7 +308,7 @@ describe("LLMClient tools", () => {
|
||||
success: Schema.Struct({ ok: Schema.Boolean }),
|
||||
execute: () => Effect.succeed({ ok: true }),
|
||||
toModelOutput: () => [
|
||||
{ type: "file", source: { type: "url", url: "https://example.test/image.png" }, mime: "image/png" },
|
||||
{ type: "file", uri: "https://example.test/image.png", mime: "image/png" },
|
||||
],
|
||||
})
|
||||
|
||||
@@ -319,12 +317,15 @@ describe("LLMClient tools", () => {
|
||||
LLMEvent.toolCall({ id: "call_remote", name: "remote", input: {} }),
|
||||
)
|
||||
|
||||
expect(dispatched.output).toBeUndefined()
|
||||
expect(dispatched.result).toEqual({
|
||||
type: "error",
|
||||
value: 'Tool file source "url" must be materialized to inline data before provider conversion',
|
||||
expect(dispatched.output).toEqual({
|
||||
structured: { ok: true },
|
||||
content: [{ type: "file", uri: "https://example.test/image.png", mime: "image/png" }],
|
||||
})
|
||||
expect(dispatched.events.map((event) => event.type)).toEqual(["tool-error", "tool-result"])
|
||||
expect(dispatched.result).toEqual({
|
||||
type: "content",
|
||||
value: [{ type: "file", uri: "https://example.test/image.png", mime: "image/png" }],
|
||||
})
|
||||
expect(dispatched.events.map((event) => event.type)).toEqual(["tool-result"])
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -357,7 +358,7 @@ describe("LLMClient tools", () => {
|
||||
type: "content" as const,
|
||||
value: [
|
||||
{ type: "text" as const, text: "Screenshot captured." },
|
||||
{ type: "media" as const, mediaType: "image/png", data: "AAAA" },
|
||||
{ type: "file" as const, uri: "data:image/png;base64,AAAA", mime: "image/png" },
|
||||
],
|
||||
}),
|
||||
})
|
||||
@@ -379,7 +380,7 @@ describe("LLMClient tools", () => {
|
||||
type: "content",
|
||||
value: [
|
||||
{ type: "text", text: "Screenshot captured." },
|
||||
{ type: "media", mediaType: "image/png", data: "AAAA" },
|
||||
{ type: "file", uri: "data:image/png;base64,AAAA", mime: "image/png" },
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user