ba93da9a93
Stream implementations move from src/providers/ to src/api/, renamed by API id (anthropic.ts -> anthropic-messages.ts, google.ts -> google-generative-ai.ts, mistral.ts -> mistral-conversations.ts, amazon-bedrock.ts -> bedrock-converse-stream.ts). Every module now exports exactly stream/streamSimple; shared helpers move alongside. New ProviderStreams dispatch contract in types.ts, lazyApi() wrapper in api/lazy.ts, and one .lazy.ts wrapper per API. Bedrock's wrapper keeps the node-only variable-specifier import and setBedrockProviderModule() (now taking ProviderStreams). providers/register-builtins.ts deleted; interim until the compat entrypoint lands, builtin api-registry registration lives in stream.ts and lazy wrappers are exported from the root barrel. Old per-API lazy exports (streamAnthropic, ...) are gone; package.json subpaths retarget to dist/api/.
67 lines
2.6 KiB
TypeScript
67 lines
2.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { convertResponsesMessages } from "../src/api/openai-responses-shared.ts";
|
|
import { getModel } from "../src/models.ts";
|
|
import type { AssistantMessage, Context, ToolResultMessage, Usage } from "../src/types.ts";
|
|
import { shortHash } from "../src/utils/hash.ts";
|
|
|
|
const COPILOT_RAW_TOOL_CALL_ID =
|
|
"call_4VnzVawQXPB9MgYib7CiQFEY|I9b95oN1wD/cHXKTw3PpRkL6KkCtzTJhUxMouMWYwHeTo2j3htzfSk7YPx2vifiIM4g3A8XXyOj8q4Bt6SLUG7gqY1E3ELkrkVQNHglRfUmWj84lqxJY+Puieb3VKyX0FB+83TUzn91cDMF/4gzt990IzqVrc+nIb9RRscRD070Du16q1glydVjWR0SBJsE6TbY/esOjFpqplogQqrajm1eI++f3eLi73R6q7hVusY0QbeFySVxABCjhN0lXB04caBe1rzHjYzul6MAXj7uq+0r17VLq+yrtyYhN12wkmFqHeqTyEei6EFPbMy24Nc+IbJlkP0OCg02W+gOnyBFcbi2ctvJFSOhSjt1CqBdqCnnhwUqXjbWiT0wh3DmLScRgTHmGkaI+oAcQQjfic65nxj+TnEkReA==";
|
|
|
|
const usage: Usage = {
|
|
input: 0,
|
|
output: 0,
|
|
cacheRead: 0,
|
|
cacheWrite: 0,
|
|
totalTokens: 0,
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
};
|
|
|
|
describe("OpenAI Responses foreign tool call ID normalization", () => {
|
|
it("hashes foreign Copilot tool item IDs into a bounded Codex-safe fc_<hash> shape", () => {
|
|
const model = getModel("openai-codex", "gpt-5.5");
|
|
const assistant: AssistantMessage = {
|
|
role: "assistant",
|
|
content: [
|
|
{
|
|
type: "toolCall",
|
|
id: COPILOT_RAW_TOOL_CALL_ID,
|
|
name: "edit",
|
|
arguments: { path: "src/styles/app.css" },
|
|
},
|
|
],
|
|
api: "openai-responses",
|
|
provider: "github-copilot",
|
|
model: "gpt-5.5",
|
|
usage,
|
|
stopReason: "toolUse",
|
|
timestamp: Date.now() - 2000,
|
|
};
|
|
const toolResult: ToolResultMessage = {
|
|
role: "toolResult",
|
|
toolCallId: COPILOT_RAW_TOOL_CALL_ID,
|
|
toolName: "edit",
|
|
content: [{ type: "text", text: "ok" }],
|
|
isError: false,
|
|
timestamp: Date.now() - 1000,
|
|
};
|
|
const context: Context = {
|
|
systemPrompt: "You are concise.",
|
|
messages: [{ role: "user", content: "Use the tool.", timestamp: Date.now() - 3000 }, assistant, toolResult],
|
|
};
|
|
|
|
const input = convertResponsesMessages(model, context, new Set(["openai", "openai-codex", "opencode"]));
|
|
const functionCall = input.find((item) => item.type === "function_call");
|
|
|
|
expect(functionCall).toBeDefined();
|
|
expect(functionCall?.type).toBe("function_call");
|
|
if (!functionCall || functionCall.type !== "function_call") {
|
|
throw new Error("Expected function_call item");
|
|
}
|
|
|
|
const expectedItemId = `fc_${shortHash(COPILOT_RAW_TOOL_CALL_ID.split("|")[1]!)}`;
|
|
expect(functionCall.id).toBe(expectedItemId);
|
|
expect(functionCall.id?.length ?? 0).toBeLessThanOrEqual(64);
|
|
expect(functionCall.id).toMatch(/^fc_[A-Za-z0-9]+$/);
|
|
});
|
|
});
|