mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-02 09:41:37 +00:00
* fix(providers): canonicalize live model families and stream finalization * test(openrouter): use canonical typed stream fixture --------- Co-authored-by: Peter Steinberger <steipete@macos.shared>
111 lines
3.1 KiB
TypeScript
111 lines
3.1 KiB
TypeScript
import {
|
|
BedrockRuntimeClient,
|
|
ConversationRole,
|
|
StopReason as BedrockStopReason,
|
|
} from "@aws-sdk/client-bedrock-runtime";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { streamSimpleBedrock } from "./stream.runtime.js";
|
|
|
|
const model = {
|
|
api: "bedrock-converse-stream",
|
|
provider: "amazon-bedrock",
|
|
id: "amazon.nova-micro-v1:0",
|
|
name: "Nova Micro",
|
|
baseUrl: "https://bedrock-runtime.us-east-1.amazonaws.com",
|
|
reasoning: true,
|
|
input: ["text"],
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
contextWindow: 128_000,
|
|
maxTokens: 4096,
|
|
} as const;
|
|
|
|
async function* events(items: unknown[]) {
|
|
yield* items;
|
|
}
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe("Bedrock provider-owned stream lifecycle", () => {
|
|
it.each([
|
|
{
|
|
label: "text",
|
|
blocks: [{ contentBlockDelta: { contentBlockIndex: 0, delta: { text: "ready" } } }],
|
|
endEvent: "text_end",
|
|
stopReason: BedrockStopReason.END_TURN,
|
|
},
|
|
{
|
|
label: "thinking",
|
|
blocks: [
|
|
{
|
|
contentBlockDelta: {
|
|
contentBlockIndex: 0,
|
|
delta: { reasoningContent: { text: "considered" } },
|
|
},
|
|
},
|
|
],
|
|
endEvent: "thinking_end",
|
|
stopReason: BedrockStopReason.END_TURN,
|
|
},
|
|
{
|
|
label: "redacted thinking",
|
|
blocks: [
|
|
{
|
|
contentBlockDelta: {
|
|
contentBlockIndex: 0,
|
|
delta: { reasoningContent: { redactedContent: new Uint8Array([1, 2, 3]) } },
|
|
},
|
|
},
|
|
],
|
|
endEvent: "thinking_end",
|
|
stopReason: BedrockStopReason.END_TURN,
|
|
},
|
|
{
|
|
label: "tool call",
|
|
blocks: [
|
|
{
|
|
contentBlockStart: {
|
|
contentBlockIndex: 0,
|
|
start: { toolUse: { toolUseId: "call_lookup", name: "lookup" } },
|
|
},
|
|
},
|
|
{
|
|
contentBlockDelta: {
|
|
contentBlockIndex: 0,
|
|
delta: { toolUse: { input: '{"query":"ready"}' } },
|
|
},
|
|
},
|
|
],
|
|
endEvent: "toolcall_end",
|
|
stopReason: BedrockStopReason.TOOL_USE,
|
|
},
|
|
])("finalizes the active $label block at the provider terminal boundary", async (scenario) => {
|
|
vi.spyOn(BedrockRuntimeClient.prototype, "send").mockResolvedValue({
|
|
$metadata: { httpStatusCode: 200 },
|
|
stream: events([
|
|
{ messageStart: { role: ConversationRole.ASSISTANT } },
|
|
...scenario.blocks,
|
|
{ messageStop: { stopReason: scenario.stopReason } },
|
|
]),
|
|
} as never);
|
|
|
|
const stream = streamSimpleBedrock(model as never, {
|
|
messages: [{ role: "user", content: "Continue", timestamp: 0 }],
|
|
});
|
|
const observed = [];
|
|
for await (const event of stream) {
|
|
observed.push(event.type);
|
|
}
|
|
const output = await stream.result();
|
|
|
|
expect(observed.at(-2)).toBe(scenario.endEvent);
|
|
expect(observed.at(-1)).toBe("done");
|
|
expect(output.content[0]).not.toHaveProperty("index");
|
|
expect(output.content[0]).not.toHaveProperty("partialJson");
|
|
if (scenario.label === "redacted thinking") {
|
|
expect(output.content[0]).toMatchObject({ redacted: true, thinkingSignature: "AQID" });
|
|
}
|
|
});
|
|
});
|