mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 10:52:17 +03:00
Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437, ESLint 0 erros nos 152 arquivos alterados, e a suíte vitest:ui completa (2149) verde. Sobre esta PR especificamente: rodei os **23 arquivos de teste** que ela toca sobre o tip final, depois do merge da base — **392/392**. A migration `174_server_tool_executions.sql` não colide (o tip está em 173, e você já a renumerou em `c35f0fd7`). O dono foi consultado antes do merge, porque o loop está atrás da flag `SERVER_OWNED_TOOL_LOOP_ENABLED` mas o primeiro send não-streaming mudou de dono sem flag, e a verificação manual em combo com Memory continuava desmarcada. A condição dele foi: entra se os testes focados passarem aqui. Passaram. O lock de passthrough (`fetchCalls.length === 1`) é a parte que mais me convenceu — o double-dispatch que um `if (stream)` em volta do send existente causaria é exatamente o tipo de regressão que não aparece em teste de comportamento, só em contagem de chamada. **Três ajustes meus na sua branch:** 1. `tests/unit/chatcore-stream-error-result.test.ts` procurava `"const legResult = await runNonStreamingProviderLeg"`, mas o seu commit final `6077b9dd` passou a reatribuir `legResult` e trocou para `let`. O guard falhava na sua própria branch (confirmei que o arquivo e o `chatCore.ts` eram byte-idênticos ao head da PR, então não era efeito da leva). Passou a aceitar `const|let` — a intenção do guard é o try/catch em volta da chamada, não a palavra-chave. 2. `tests/integration/skills-pipeline.test.ts` foi de 1156 para 1338 linhas e estourou o `testCap` de 1200. Segui o mesmo caminho que você já tinha tomado em `a1d2d20d` para os testes unitários: extraí os três casos do server-owned tool loop para `tests/integration/server-owned-tool-loop-pipeline.test.ts` (259 linhas), com instância própria do harness. O glob `tests/integration/*.test.ts` pega o arquivo novo sem registro adicional. 3/3 verdes isolados. 3. O arquivo novo herdou cinco `any` do original — que só passavam por estarem congelados no `eslint-suppressions.json` sob o nome antigo. Tipei como `Record<string, unknown>`. E `tests/unit/non-streaming-finalization.test.ts` tinha dois argumentos não usados em `trackPendingRequest`, agora prefixados com `_`. Nada disso toca produção nem enfraquece asserção.
220 lines
6.3 KiB
TypeScript
220 lines
6.3 KiB
TypeScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import type {
|
|
ChatCoreErrorResult,
|
|
ProviderLegReceipt,
|
|
ProviderLegUsage,
|
|
ServerOwnedToolLoopResult,
|
|
} from "../../src/lib/skills/toolLoopTypes.ts";
|
|
import {
|
|
buildNonStreamingFinalizationPlan,
|
|
finalizeNonStreamingRequest,
|
|
finalizeToolLoopError,
|
|
type NonStreamingFinalizationDeps,
|
|
} from "../../open-sse/handlers/chatCore/nonStreamingFinalization.ts";
|
|
|
|
function receipt(index: number, overrides: Partial<ProviderLegReceipt> = {}): ProviderLegReceipt {
|
|
return {
|
|
index,
|
|
connectionId: "conn-1",
|
|
provider: "openai",
|
|
model: "gpt-4o",
|
|
startedAt: "2026-01-01T00:00:00.000Z",
|
|
endedAt: "2026-01-01T00:00:01.000Z",
|
|
latencyMs: 100,
|
|
httpStatus: 200,
|
|
errorType: null,
|
|
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
|
|
serviceTier: null,
|
|
computedCostUsd: 0.01,
|
|
toolCalls: [],
|
|
termination: "completed",
|
|
clientVisible: true,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function usage(overrides: Partial<ProviderLegUsage> = {}): ProviderLegUsage {
|
|
return {
|
|
prompt_tokens: 100,
|
|
completion_tokens: 20,
|
|
total_tokens: 120,
|
|
cache_read_input_tokens: 10,
|
|
reasoning_tokens: 5,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function errorResult(): ChatCoreErrorResult {
|
|
return {
|
|
success: false,
|
|
status: 429,
|
|
response: new Response(JSON.stringify({ error: { message: "rate limited" } }), {
|
|
status: 429,
|
|
}),
|
|
error: "rate limited",
|
|
errorCode: "rate_limited",
|
|
errorType: "rate_limit_error",
|
|
retryAfterMs: 1000,
|
|
};
|
|
}
|
|
|
|
function spyDeps(): NonStreamingFinalizationDeps & { calls: Record<string, number> } {
|
|
const calls = {
|
|
writeUsage: 0,
|
|
writeCost: 0,
|
|
scheduleQuota: 0,
|
|
writeAttempt: 0,
|
|
finalizePending: 0,
|
|
};
|
|
return {
|
|
calls,
|
|
writeUsage: () => {
|
|
calls.writeUsage += 1;
|
|
},
|
|
writeCost: () => {
|
|
calls.writeCost += 1;
|
|
},
|
|
scheduleQuota: () => {
|
|
calls.scheduleQuota += 1;
|
|
},
|
|
writeAttempt: () => {
|
|
calls.writeAttempt += 1;
|
|
},
|
|
finalizePending: () => {
|
|
calls.finalizePending += 1;
|
|
},
|
|
};
|
|
}
|
|
|
|
test("failure plan maps loop aggregate and 429 error", () => {
|
|
const loop: ServerOwnedToolLoopResult = {
|
|
kind: "error",
|
|
errorResult: errorResult(),
|
|
cumulativeUsage: usage(),
|
|
totalCostUsd: 0.03,
|
|
receipts: [
|
|
receipt(0),
|
|
receipt(1, {
|
|
httpStatus: 429,
|
|
errorType: "rate_limit_error",
|
|
termination: "provider_error",
|
|
computedCostUsd: 0.02,
|
|
}),
|
|
],
|
|
followUps: 1,
|
|
termination: "provider_error",
|
|
};
|
|
const plan = buildNonStreamingFinalizationPlan(loop);
|
|
assert.equal(plan.kind, "failure");
|
|
if (plan.kind !== "failure") return;
|
|
assert.equal(plan.error.status, 429);
|
|
assert.equal(plan.error.errorCode, "rate_limited");
|
|
assert.deepEqual(plan.usage, usage());
|
|
assert.equal(plan.totalCostUsd, 0.03);
|
|
assert.equal(plan.receiptCount, 2);
|
|
});
|
|
|
|
test("success plan maps loop usage and receipt count", () => {
|
|
const loop: ServerOwnedToolLoopResult = {
|
|
kind: "ok",
|
|
response: { choices: [{ message: { content: "done" }, finish_reason: "stop" }] },
|
|
cumulativeUsage: usage({ prompt_tokens: 11, completion_tokens: 7, total_tokens: 18 }),
|
|
totalCostUsd: 0.02,
|
|
receipts: [receipt(0)],
|
|
followUps: 0,
|
|
termination: "completed",
|
|
};
|
|
const plan = buildNonStreamingFinalizationPlan(loop);
|
|
assert.equal(plan.kind, "success");
|
|
if (plan.kind !== "success") return;
|
|
assert.equal(plan.usage?.prompt_tokens, 11);
|
|
assert.equal(plan.totalCostUsd, 0.02);
|
|
assert.equal(plan.receiptCount, 1);
|
|
});
|
|
|
|
test("failure finalizer writes usage/cost/attempt/pending once and skips quota", async () => {
|
|
const loop: ServerOwnedToolLoopResult = {
|
|
kind: "error",
|
|
errorResult: errorResult(),
|
|
cumulativeUsage: usage(),
|
|
totalCostUsd: 0.03,
|
|
receipts: [receipt(0), receipt(1, { httpStatus: 429 })],
|
|
followUps: 1,
|
|
termination: "provider_error",
|
|
};
|
|
const deps = spyDeps();
|
|
await finalizeNonStreamingRequest(buildNonStreamingFinalizationPlan(loop), deps);
|
|
assert.equal(deps.calls.writeUsage, 1);
|
|
assert.equal(deps.calls.writeCost, 1);
|
|
assert.equal(deps.calls.scheduleQuota, 0);
|
|
assert.equal(deps.calls.writeAttempt, 1);
|
|
assert.equal(deps.calls.finalizePending, 1);
|
|
});
|
|
|
|
test("success finalizer writes usage/cost/quota/attempt/pending once", async () => {
|
|
const loop: ServerOwnedToolLoopResult = {
|
|
kind: "ok",
|
|
response: { choices: [{ message: { content: "done" }, finish_reason: "stop" }] },
|
|
cumulativeUsage: usage(),
|
|
totalCostUsd: 0.02,
|
|
receipts: [receipt(0)],
|
|
followUps: 0,
|
|
termination: "completed",
|
|
};
|
|
const deps = spyDeps();
|
|
await finalizeNonStreamingRequest(buildNonStreamingFinalizationPlan(loop), deps);
|
|
assert.equal(deps.calls.writeUsage, 1);
|
|
assert.equal(deps.calls.writeCost, 1);
|
|
assert.equal(deps.calls.scheduleQuota, 1);
|
|
assert.equal(deps.calls.writeAttempt, 1);
|
|
assert.equal(deps.calls.finalizePending, 1);
|
|
});
|
|
|
|
test("finalizeToolLoopError delegates through finalization plan and deps", async () => {
|
|
const loop: ServerOwnedToolLoopResult = {
|
|
kind: "error",
|
|
errorResult: errorResult(),
|
|
cumulativeUsage: usage(),
|
|
totalCostUsd: 0.05,
|
|
receipts: [receipt(0), receipt(1, { httpStatus: 429 })],
|
|
followUps: 1,
|
|
termination: "provider_error",
|
|
};
|
|
let usageSaved = false;
|
|
let attemptLogged = false;
|
|
let pendingTracked = false;
|
|
|
|
const res = await finalizeToolLoopError({
|
|
loop,
|
|
model: "gpt-4o",
|
|
provider: "openai",
|
|
connectionId: "conn-1",
|
|
providerRequest: { messages: [] },
|
|
persistFailureUsage: (status, code, u) => {
|
|
assert.equal(status, 429);
|
|
assert.equal(code, "rate_limited");
|
|
assert.equal(u?.prompt_tokens, 100);
|
|
assert.equal(u?.completion_tokens, 20);
|
|
assert.equal(u?.cache_read_input_tokens, 10);
|
|
assert.equal(u?.reasoning_tokens, 5);
|
|
usageSaved = true;
|
|
},
|
|
persistAttemptLogs: (params) => {
|
|
assert.equal(params.status, 429);
|
|
attemptLogged = true;
|
|
},
|
|
trackPendingRequest: (m, _p, _conn, pending) => {
|
|
assert.equal(m, "gpt-4o");
|
|
assert.equal(pending, false);
|
|
pendingTracked = true;
|
|
},
|
|
});
|
|
|
|
assert.equal(res.status, 429);
|
|
assert.equal(usageSaved, true);
|
|
assert.equal(attemptLogged, true);
|
|
assert.equal(pendingTracked, true);
|
|
});
|