Merge branch 'bedrock-messages' into native-compaction

This commit is contained in:
Shoubhit Dash 2026-08-31 22:09:51 +05:30
commit e07ae57caa
2 changed files with 87 additions and 3 deletions

View file

@ -16,7 +16,12 @@ const Body = Schema.Struct({
...Struct.omit(AnthropicMessages.AnthropicMessagesBody.fields, ["model", "stream"]),
anthropic_version: Schema.Literal(VERSION),
anthropic_beta: Schema.optional(Schema.Array(Schema.String)),
})
}).check(
Schema.makeFilter(
(body) =>
body.messages.flatMap((message) => message.content.map(mediaIssue)).find((issue) => issue !== undefined) ?? true,
),
)
const Event = Schema.Struct({
chunk: Schema.optional(Schema.Struct({ bytes: Schema.String })),
exception: Schema.optional(
@ -80,6 +85,25 @@ export const protocol = Protocol.make({
},
})
function mediaIssue(
block: AnthropicMessages.AnthropicMessagesBody["messages"][number]["content"][number],
): string | undefined {
if (block.type === "tool_result")
return typeof block.content === "string"
? undefined
: block.content.map(mediaIssue).find((issue) => issue !== undefined)
if (block.type !== "image" && block.type !== "document") return
if (block.source.type === "url" || block.source.type === "file")
return "Bedrock Messages does not support URL or file-ID media sources"
if (
block.type === "image" &&
!["image/jpeg", "image/png", "image/webp", "image/gif"].includes(block.source.media_type)
)
return "Bedrock Messages requires a JPEG, PNG, WebP, or GIF image"
if (block.source.type === "base64" && Encoding.decodeBase64(block.source.data)._tag === "Failure")
return "Bedrock Messages media data must be valid base64"
}
export const route = Route.make({
id: ID,
provider: "amazon-bedrock",

View file

@ -3,9 +3,9 @@ import { fromUtf8, toUtf8 } from "@smithy/util-utf8"
import { expect } from "bun:test"
import { Effect } from "effect"
import { LLM, LLMRequest, Message } from "../../src/index.js"
import { LLMClient } from "../../src/route/client.js"
import { LLMClient, compileRequest } from "../../src/route/client.js"
import { AmazonBedrock } from "../../src/providers/index.js"
import { testEffect } from "../lib/effect.js"
import { it, testEffect } from "../lib/effect.js"
import { dynamicResponse, fixedResponse } from "../lib/http.js"
const codec = new EventStreamCodec(toUtf8, fromUtf8)
@ -91,3 +91,63 @@ testEffect(
expect(error.reason.http?.status).toBe(200)
}),
)
for (const mediaType of ["image/png", "application/pdf"]) {
for (const data of ["https://example.com/media", "invalid base64!"]) {
for (const role of ["user", "tool"] as const) {
it.effect(`rejects ${role} ${mediaType} with ${data}`, () =>
Effect.gen(function* () {
const error = yield* compileRequest(
LLM.request({
model: AmazonBedrock.configure({ apiKey: "test" }).messages("claude"),
messages:
role === "user"
? [Message.user({ type: "media", mediaType, data })]
: [
Message.tool({
id: "call_1",
name: "read",
result: { type: "content", value: [{ type: "file", mime: mediaType, uri: data }] },
}),
],
}),
).pipe(Effect.flip)
expect(error.reason._tag).toBe("InvalidRequest")
expect(error.message).toContain("Bedrock Messages")
}),
)
}
}
}
it.effect("accepts inline image and document sources", () =>
Effect.gen(function* () {
const prepared = yield* compileRequest(
LLM.request({
model: AmazonBedrock.configure({ apiKey: "test" }).messages("claude"),
messages: [
Message.user([
{ type: "media", mediaType: "image/png", data: "data:image/png;base64,AQID" },
{ type: "media", mediaType: "application/pdf", data: "data:application/pdf;base64,AQID" },
]),
],
}),
)
expect(prepared.body.messages[0].content.map((block: { source: { type: string } }) => block.source.type)).toEqual([
"base64",
"base64",
])
}),
)
it.effect("rejects Anthropic file IDs", () =>
Effect.gen(function* () {
const error = yield* compileRequest(
LLM.request({
model: AmazonBedrock.configure({ apiKey: "test" }).messages("claude"),
messages: [Message.user({ type: "media", mediaType: "image/png", data: "", metadata: { file_id: "file_1" } })],
}),
).pipe(Effect.flip)
expect(error.message).toContain("file-ID")
}),
)