mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-13 18:32:12 +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.
150 lines
5.9 KiB
TypeScript
150 lines
5.9 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import { aggregateProviderLegUsage } from "../../src/lib/skills/serverOwnedToolLoop.ts";
|
|
import type { ProviderLegUsage } from "../../src/lib/skills/toolLoopTypes.ts";
|
|
|
|
// ─── Basic aggregation ────────────────────────────────────────────────────────
|
|
|
|
test("aggregateProviderLegUsage: sums prompt_tokens, completion_tokens, total_tokens", () => {
|
|
const a: ProviderLegUsage = {
|
|
prompt_tokens: 100,
|
|
completion_tokens: 20,
|
|
total_tokens: 120,
|
|
};
|
|
const b: ProviderLegUsage = {
|
|
prompt_tokens: 150,
|
|
completion_tokens: 80,
|
|
total_tokens: 230,
|
|
};
|
|
|
|
const result = aggregateProviderLegUsage([a, b]);
|
|
assert.strictEqual(result.prompt_tokens, 250);
|
|
assert.strictEqual(result.completion_tokens, 100);
|
|
assert.strictEqual(result.total_tokens, 350, "total_tokens rederived as prompt+completion");
|
|
});
|
|
|
|
// ─── Optional fields: present in at least one leg ────────────────────────────
|
|
|
|
test("aggregateProviderLegUsage: optional fields summed when present", () => {
|
|
const a: ProviderLegUsage = {
|
|
prompt_tokens: 10,
|
|
completion_tokens: 5,
|
|
total_tokens: 15,
|
|
cached_tokens: 3,
|
|
cache_read_input_tokens: 2,
|
|
cache_creation_input_tokens: 1,
|
|
reasoning_tokens: 4,
|
|
cost_in_usd_ticks: 100,
|
|
};
|
|
const b: ProviderLegUsage = {
|
|
prompt_tokens: 10,
|
|
completion_tokens: 5,
|
|
total_tokens: 15,
|
|
// no optional fields
|
|
};
|
|
|
|
const result = aggregateProviderLegUsage([a, b]);
|
|
assert.strictEqual(result.prompt_tokens, 20);
|
|
assert.strictEqual(result.completion_tokens, 10);
|
|
assert.strictEqual(result.total_tokens, 30);
|
|
assert.strictEqual(result.cached_tokens, 3);
|
|
assert.strictEqual(result.cache_read_input_tokens, 2);
|
|
assert.strictEqual(result.cache_creation_input_tokens, 1);
|
|
assert.strictEqual(result.reasoning_tokens, 4);
|
|
assert.strictEqual(result.cost_in_usd_ticks, 100);
|
|
});
|
|
|
|
// ─── All-null usage → null ───────────────────────────────────────────────────
|
|
|
|
test("aggregateProviderLegUsage: all null usages → returns zero usage (not null)", () => {
|
|
const result = aggregateProviderLegUsage([null, null]);
|
|
assert.strictEqual(result.prompt_tokens, 0);
|
|
assert.strictEqual(result.completion_tokens, 0);
|
|
assert.strictEqual(result.total_tokens, 0);
|
|
});
|
|
|
|
test("aggregateProviderLegUsage: mix of null and non-null → sums only non-null", () => {
|
|
const a: ProviderLegUsage = {
|
|
prompt_tokens: 100,
|
|
completion_tokens: 20,
|
|
total_tokens: 120,
|
|
};
|
|
|
|
const result = aggregateProviderLegUsage([a, null]);
|
|
assert.strictEqual(result.prompt_tokens, 100);
|
|
assert.strictEqual(result.completion_tokens, 20);
|
|
assert.strictEqual(result.total_tokens, 120);
|
|
});
|
|
|
|
// ─── total_tokens rederived ───────────────────────────────────────────────────
|
|
|
|
test("aggregateProviderLegUsage: total_tokens is always prompt+completion, not sum of raw total_tokens", () => {
|
|
const a: ProviderLegUsage = {
|
|
prompt_tokens: 100,
|
|
completion_tokens: 20,
|
|
total_tokens: 999, // wrong raw value
|
|
};
|
|
const b: ProviderLegUsage = {
|
|
prompt_tokens: 100,
|
|
completion_tokens: 20,
|
|
total_tokens: 999, // wrong raw value
|
|
};
|
|
|
|
const result = aggregateProviderLegUsage([a, b]);
|
|
assert.strictEqual(result.total_tokens, 240, "100+20 + 100+20 = 240, not 1998");
|
|
});
|
|
|
|
// ─── Optional fields absent when no leg defines them ──────────────────────────
|
|
|
|
test("aggregateProviderLegUsage: optional fields absent when no leg defines them", () => {
|
|
const a: ProviderLegUsage = {
|
|
prompt_tokens: 10,
|
|
completion_tokens: 5,
|
|
total_tokens: 15,
|
|
};
|
|
|
|
const result = aggregateProviderLegUsage([a]);
|
|
assert.strictEqual(result.cached_tokens, undefined);
|
|
assert.strictEqual(result.cache_read_input_tokens, undefined);
|
|
assert.strictEqual(result.cache_creation_input_tokens, undefined);
|
|
assert.strictEqual(result.reasoning_tokens, undefined);
|
|
assert.strictEqual(result.cost_in_usd_ticks, undefined);
|
|
});
|
|
|
|
// ─── Empty array ──────────────────────────────────────────────────────────────
|
|
|
|
test("aggregateProviderLegUsage: empty array → zeros", () => {
|
|
const result = aggregateProviderLegUsage([]);
|
|
assert.strictEqual(result.prompt_tokens, 0);
|
|
assert.strictEqual(result.completion_tokens, 0);
|
|
assert.strictEqual(result.total_tokens, 0);
|
|
});
|
|
|
|
// ─── Claude-style alias normalization is caller's responsibility ──────────────
|
|
|
|
test("aggregateProviderLegUsage: Claude input_tokens/output_tokens are NOT aliases — caller must normalize before aggregation", () => {
|
|
// Claude uses input_tokens/output_tokens, not prompt_tokens/completion_tokens.
|
|
// The caller must normalize before passing to aggregateProviderLegUsage.
|
|
// This test verifies that aggregateProviderLegUsage uses the standard field names.
|
|
const a: ProviderLegUsage = {
|
|
prompt_tokens: 100,
|
|
completion_tokens: 20,
|
|
total_tokens: 120,
|
|
};
|
|
|
|
const result = aggregateProviderLegUsage([a]);
|
|
assert.strictEqual(result.prompt_tokens, 100);
|
|
assert.strictEqual(result.completion_tokens, 20);
|
|
// If someone passes input_tokens (Claude alias), it would be ignored
|
|
const claudeStyle = {
|
|
prompt_tokens: 0,
|
|
completion_tokens: 0,
|
|
total_tokens: 0,
|
|
input_tokens: 50,
|
|
output_tokens: 10,
|
|
} as unknown as ProviderLegUsage;
|
|
const result2 = aggregateProviderLegUsage([claudeStyle]);
|
|
assert.strictEqual(result2.prompt_tokens, 0, "Claude alias not summed into prompt_tokens");
|
|
});
|