Files
OmniRoute/tests/unit/tool-use-id-sanitization.test.ts
Praveen K Palaniswamy 65e81158ab fix(ollama): route models by advertised capability (#11088)
Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host.

Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean.

Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes #11087.
2026-08-23 11:45:01 -03:00

134 lines
4.2 KiB
TypeScript

import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { prepareClaudeRequest } from "../../open-sse/translator/helpers/claudeHelper.ts";
import { openaiToClaudeResponse } from "../../open-sse/translator/response/openai-to-claude.ts";
import { translateNonStreamingResponse } from "../../open-sse/handlers/responseTranslator.ts";
describe("tool_use.id sanitization", () => {
describe("prepareClaudeRequest (passthrough defense)", () => {
it("sanitizes invalid characters in tool_use.id and tool_result.tool_use_id symmetrically", () => {
const input = {
messages: [
{
role: "assistant",
content: [
{
type: "tool_use",
id: "call:123.abc:xyz#456",
name: "test_tool",
input: {},
},
],
},
{
role: "user",
content: [
{
type: "tool_result",
tool_use_id: "call:123.abc:xyz#456",
content: "ok",
},
],
},
],
};
const result = prepareClaudeRequest(input);
const assistantMsg = result.messages?.[0];
const userMsg = result.messages?.[1];
const toolUseBlock = Array.isArray(assistantMsg?.content)
? assistantMsg.content.find((b) => b.type === "tool_use")
: null;
const toolResultBlock = Array.isArray(userMsg?.content)
? userMsg.content.find((b) => b.type === "tool_result")
: null;
assert.equal(toolUseBlock?.id, "call_123_abc_xyz_456");
assert.equal(toolResultBlock?.tool_use_id, "call_123_abc_xyz_456");
});
});
describe("openaiToClaudeResponse (streaming response translator)", () => {
it("sanitizes tc.id when emitting content_block_start for tool_use", () => {
const state = {
toolCalls: new Map(),
toolNameMap: new Map(),
nextBlockIndex: 0,
textBuffer: "",
textEmitted: false,
reasoningBuffer: "",
reasoningEmitted: false,
thinkingEmitted: false,
usage: { input_tokens: 0, output_tokens: 0 },
messageId: "msg_123",
finishReason: null,
};
const chunk = {
id: "chatcmpl-123",
choices: [
{
index: 0,
delta: {
tool_calls: [
{
index: 0,
id: "call:999.invalid:id#1",
type: "function",
function: { name: "my_func", arguments: "{}" },
},
],
},
},
],
};
const events = openaiToClaudeResponse(chunk, state) as Array<Record<string, unknown>>;
const startBlock = events?.find((e) => e.type === "content_block_start") as
{ content_block: { id: string } } | undefined;
assert.ok(startBlock, "should emit content_block_start");
assert.equal(startBlock.content_block.id, "call_999_invalid_id_1");
});
});
describe("translateNonStreamingResponse (non-streaming response translator)", () => {
it("sanitizes tool_calls[].id when mapping to tool_use content blocks", () => {
const response = {
id: "chatcmpl-456",
object: "chat.completion",
created: 1234567890,
model: "gpt-4o",
choices: [
{
index: 0,
message: {
role: "assistant",
content: null,
tool_calls: [
{
id: "call:invalid.id:777#test",
type: "function",
function: { name: "get_weather", arguments: "{}" },
},
],
},
finish_reason: "tool_calls",
},
],
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
};
const claudeResp = translateNonStreamingResponse(response, "openai", "claude") as {
content?: Array<Record<string, unknown>>;
};
const toolUseBlock = claudeResp?.content?.find((b) => b.type === "tool_use");
assert.ok(toolUseBlock, "should contain tool_use block");
assert.equal(toolUseBlock?.id, "call_invalid_id_777_test");
});
});
});