mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-07 15:52:52 +03:00
Validated in local merge-train (devbox-vm-06-dev002) @ combined-tip (FAST gates green: static + changed tests + vitest — only pre-existing audit.test.ts flake). Evidence: /home/diegosouzapw/dev/proxys/OmniRoute/.claude/worktrees/merge-train-20260805-213228-suite.log
199 lines
6.4 KiB
TypeScript
199 lines
6.4 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import { buildKiroPayload } from "../../open-sse/translator/request/openai-to-kiro.ts";
|
|
|
|
// Kiro rejects a `toolSpecification.description` longer than ~10000 chars, so the
|
|
// translator relocates an oversized description into the current turn's content
|
|
// and leaves a pointer in the schema (mirroring kiro-gateway's
|
|
// `converters_core.py::process_tools_with_long_descriptions`).
|
|
//
|
|
// The relocation used to be carried on the message object as `_toolDocs`, which
|
|
// broke in two ways: the tool-bearing user turn is moved into `history` on every
|
|
// multi-turn request, so the docs were dropped and the model saw only the
|
|
// pointer; and the field leaked into the upstream payload, which Kiro rejects
|
|
// because it refuses unknown top-level keys.
|
|
|
|
const POINTER = "[Full documentation in system prompt under '## Tool: big_tool']";
|
|
const DOCS_HEADING = "# Tool Documentation";
|
|
|
|
function bigToolWithDescriptionLength(length: number) {
|
|
return [
|
|
{
|
|
type: "function",
|
|
function: {
|
|
name: "big_tool",
|
|
description: "D".repeat(length),
|
|
parameters: { type: "object", properties: {} },
|
|
},
|
|
},
|
|
];
|
|
}
|
|
|
|
const TURN_SHAPES = {
|
|
"single user turn": [{ role: "user", content: "a" }],
|
|
"multi-turn conversation": [
|
|
{ role: "user", content: "a" },
|
|
{ role: "assistant", content: "b" },
|
|
{ role: "user", content: "c" },
|
|
],
|
|
"deep multi-turn conversation": [
|
|
{ role: "user", content: "a" },
|
|
{ role: "assistant", content: "b" },
|
|
{ role: "user", content: "c" },
|
|
{ role: "assistant", content: "d" },
|
|
{ role: "user", content: "e" },
|
|
],
|
|
"assistant-first conversation": [
|
|
{ role: "assistant", content: "hello" },
|
|
{ role: "user", content: "hi" },
|
|
],
|
|
// No user turn at all: currentMessage is the synthesized filler turn, which
|
|
// reaches the tools schema through the fallback attachment path.
|
|
"no user messages": [{ role: "assistant", content: "only" }],
|
|
};
|
|
|
|
test("relocated tool documentation reaches the current turn for every turn shape", () => {
|
|
for (const [label, messages] of Object.entries(TURN_SHAPES)) {
|
|
const payload = buildKiroPayload(
|
|
"claude-sonnet-4.5",
|
|
{ messages, tools: bigToolWithDescriptionLength(12000) },
|
|
true,
|
|
{}
|
|
);
|
|
const current = payload.conversationState.currentMessage.userInputMessage;
|
|
|
|
assert.ok(
|
|
current.content.includes(DOCS_HEADING),
|
|
`${label}: full tool documentation must be prepended to the current turn`
|
|
);
|
|
assert.ok(
|
|
current.content.includes("D".repeat(12000)),
|
|
`${label}: the relocated description text itself must survive`
|
|
);
|
|
assert.equal(
|
|
current.userInputMessageContext?.tools[0].toolSpecification.description,
|
|
POINTER,
|
|
`${label}: the oversized description must be replaced by the pointer`
|
|
);
|
|
}
|
|
});
|
|
|
|
test("relocation never leaks an unknown field into the upstream payload", () => {
|
|
for (const [label, messages] of Object.entries(TURN_SHAPES)) {
|
|
const payload = buildKiroPayload(
|
|
"claude-sonnet-4.5",
|
|
{ messages, tools: bigToolWithDescriptionLength(12000) },
|
|
true,
|
|
{}
|
|
);
|
|
|
|
assert.ok(
|
|
!JSON.stringify(payload).includes("_toolDocs"),
|
|
`${label}: Kiro rejects unknown top-level keys, so _toolDocs must not be serialized`
|
|
);
|
|
}
|
|
});
|
|
|
|
test("a description within the limit is left alone and adds no documentation block", () => {
|
|
const payload = buildKiroPayload(
|
|
"claude-sonnet-4.5",
|
|
{
|
|
messages: TURN_SHAPES["multi-turn conversation"],
|
|
tools: [
|
|
{
|
|
type: "function",
|
|
function: {
|
|
name: "ok_tool",
|
|
description: "short desc",
|
|
parameters: { type: "object", properties: {} },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
true,
|
|
{}
|
|
);
|
|
const current = payload.conversationState.currentMessage.userInputMessage;
|
|
|
|
assert.equal(
|
|
current.userInputMessageContext?.tools[0].toolSpecification.description,
|
|
"short desc",
|
|
"a description under the limit must reach Kiro verbatim"
|
|
);
|
|
assert.ok(
|
|
!current.content.includes(DOCS_HEADING),
|
|
"no documentation block should be injected when nothing was relocated"
|
|
);
|
|
});
|
|
|
|
// A description exactly at the limit must pass through: the guard triggers only
|
|
// above it, and an off-by-one here would relocate a description Kiro accepts.
|
|
test("the relocation boundary triggers above the limit, not at it", () => {
|
|
const messages = TURN_SHAPES["multi-turn conversation"];
|
|
|
|
const atLimit = buildKiroPayload(
|
|
"claude-sonnet-4.5",
|
|
{ messages, tools: bigToolWithDescriptionLength(10000) },
|
|
true,
|
|
{}
|
|
);
|
|
const atLimitCurrent = atLimit.conversationState.currentMessage.userInputMessage;
|
|
assert.equal(
|
|
atLimitCurrent.userInputMessageContext?.tools[0].toolSpecification.description.length,
|
|
10000,
|
|
"a description exactly at the limit must not be relocated"
|
|
);
|
|
assert.ok(
|
|
!atLimitCurrent.content.includes(DOCS_HEADING),
|
|
"no documentation block at the boundary"
|
|
);
|
|
|
|
const overLimit = buildKiroPayload(
|
|
"claude-sonnet-4.5",
|
|
{ messages, tools: bigToolWithDescriptionLength(10001) },
|
|
true,
|
|
{}
|
|
);
|
|
assert.equal(
|
|
overLimit.conversationState.currentMessage.userInputMessage.userInputMessageContext?.tools[0]
|
|
.toolSpecification.description,
|
|
POINTER,
|
|
"one char over the limit must be relocated"
|
|
);
|
|
});
|
|
|
|
// Only the oversized tool is relocated; a mixed inventory must keep the short
|
|
// descriptions inline so the model still sees them next to the schema.
|
|
test("only oversized descriptions are relocated in a mixed tool inventory", () => {
|
|
const payload = buildKiroPayload(
|
|
"claude-sonnet-4.5",
|
|
{
|
|
messages: TURN_SHAPES["multi-turn conversation"],
|
|
tools: [
|
|
{
|
|
type: "function",
|
|
function: {
|
|
name: "small_tool",
|
|
description: "compact",
|
|
parameters: { type: "object", properties: {} },
|
|
},
|
|
},
|
|
...bigToolWithDescriptionLength(12000),
|
|
],
|
|
},
|
|
true,
|
|
{}
|
|
);
|
|
const current = payload.conversationState.currentMessage.userInputMessage;
|
|
const specs = current.userInputMessageContext?.tools;
|
|
|
|
assert.equal(specs[0].toolSpecification.description, "compact");
|
|
assert.equal(specs[1].toolSpecification.description, POINTER);
|
|
assert.ok(current.content.includes("## Tool: big_tool"));
|
|
assert.ok(
|
|
!current.content.includes("## Tool: small_tool"),
|
|
"a tool that was never relocated must not get a documentation section"
|
|
);
|
|
});
|