import { describe, expect, it, vi } from "vitest"; const bedrockMock = vi.hoisted(() => ({ constructorCalls: [] as Array>, })); vi.mock("@aws-sdk/client-bedrock-runtime", () => { class BedrockRuntimeServiceException extends Error {} class BedrockRuntimeClient { constructor(config: Record) { bedrockMock.constructorCalls.push(config); } send(): Promise { return Promise.reject(new Error("mock send")); } } class ConverseStreamCommand { readonly input: unknown; constructor(input: unknown) { this.input = input; } } return { BedrockRuntimeClient, BedrockRuntimeServiceException, ConverseStreamCommand, StopReason: { END_TURN: "end_turn", STOP_SEQUENCE: "stop_sequence", MAX_TOKENS: "max_tokens", MODEL_CONTEXT_WINDOW_EXCEEDED: "model_context_window_exceeded", TOOL_USE: "tool_use", }, CachePointType: { DEFAULT: "default" }, CacheTTL: { ONE_HOUR: "ONE_HOUR" }, ConversationRole: { ASSISTANT: "assistant", USER: "user" }, ImageFormat: { JPEG: "jpeg", PNG: "png", GIF: "gif", WEBP: "webp" }, ToolResultStatus: { ERROR: "error", SUCCESS: "success" }, }; }); import { getModel } from "../src/models.ts"; import { streamBedrock } from "../src/providers/amazon-bedrock.ts"; import type { Context, Message } from "../src/types.ts"; const baseModel = getModel("amazon-bedrock", "us.anthropic.claude-sonnet-4-5-20250929-v1:0"); async function capturePayload(context: Context): Promise { let capturedPayload: unknown; const s = streamBedrock(baseModel, context, { cacheRetention: "none", signal: AbortSignal.abort(), onPayload: (payload) => { capturedPayload = payload; return payload; }, }); for await (const event of s) { if (event.type === "error") break; } return capturedPayload; } describe("bedrock convertMessages skips unknown content types", () => { it("skips unknown user content blocks instead of throwing", async () => { const messages: Message[] = [ { role: "user", content: [ { type: "text", text: "hello" }, { type: "unknown", data: "foo" }, ] as any, timestamp: Date.now(), }, ]; const payload = await capturePayload({ messages }); expect(payload).toBeDefined(); const p = payload as { messages: Array<{ role: string; content: unknown[] }> }; expect(p.messages).toHaveLength(1); expect(p.messages[0].content).toHaveLength(1); expect(p.messages[0].content[0]).toEqual({ text: "hello" }); }); it("skips unknown assistant content blocks instead of throwing", async () => { const messages: Message[] = [ { role: "assistant", content: [ { type: "text", text: "hello" }, { type: "unknown", data: "foo" }, ] as any, api: "bedrock-converse-stream", provider: "amazon-bedrock", model: baseModel.id, usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, }, stopReason: "stop", timestamp: Date.now(), }, ]; const payload = await capturePayload({ messages }); expect(payload).toBeDefined(); const p = payload as { messages: Array<{ role: string; content: unknown[] }> }; expect(p.messages).toHaveLength(1); expect(p.messages[0].content).toHaveLength(1); expect(p.messages[0].content[0]).toEqual({ text: "hello" }); }); it("skips user messages with only unknown content blocks", async () => { const messages: Message[] = [ { role: "user", content: [{ type: "unknown", data: "foo" }] as any, timestamp: Date.now(), }, ]; const payload = await capturePayload({ messages }); expect(payload).toBeDefined(); const p = payload as { messages: Array<{ role: string; content: unknown[] }> }; expect(p.messages).toHaveLength(0); }); it("skips assistant messages with only unknown content blocks", async () => { const messages: Message[] = [ { role: "assistant", content: [{ type: "unknown", data: "foo" }] as any, api: "bedrock-converse-stream", provider: "amazon-bedrock", model: baseModel.id, usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, }, stopReason: "stop", timestamp: Date.now(), }, ]; const payload = await capturePayload({ messages }); expect(payload).toBeDefined(); const p = payload as { messages: Array<{ role: string; content: unknown[] }> }; expect(p.messages).toHaveLength(0); }); });