mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-17 20:32:25 +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.
134 lines
3.7 KiB
TypeScript
134 lines
3.7 KiB
TypeScript
/**
|
|
* Request-level finalization for non-streaming chat.
|
|
* Success and failure each write usage/cost/quota/attempt/pending once.
|
|
*/
|
|
|
|
import type {
|
|
ChatCoreErrorResult,
|
|
ProviderLegUsage,
|
|
ServerOwnedToolLoopResult,
|
|
} from "@/lib/skills/toolLoopTypes.ts";
|
|
import type { PersistAttemptLogsArgs } from "./attemptLogging.ts";
|
|
import { type FailureUsageAggregate, toFailureUsageAggregate } from "./failureUsage.ts";
|
|
|
|
export type NonStreamingFinalizationPlan =
|
|
| {
|
|
kind: "success";
|
|
usage: ProviderLegUsage | null;
|
|
totalCostUsd: number;
|
|
receiptCount: number;
|
|
}
|
|
| {
|
|
kind: "failure";
|
|
error: ChatCoreErrorResult;
|
|
usage: ProviderLegUsage | null;
|
|
totalCostUsd: number;
|
|
receiptCount: number;
|
|
};
|
|
|
|
export interface NonStreamingFinalizationDeps {
|
|
writeUsage: (plan: NonStreamingFinalizationPlan) => void | Promise<void>;
|
|
writeCost: (totalCostUsd: number) => void;
|
|
scheduleQuota: (plan: NonStreamingFinalizationPlan) => void | Promise<void>;
|
|
writeAttempt: (plan: NonStreamingFinalizationPlan) => void;
|
|
finalizePending: (plan: NonStreamingFinalizationPlan) => void;
|
|
}
|
|
|
|
function missingError(): ChatCoreErrorResult {
|
|
return {
|
|
success: false,
|
|
status: 500,
|
|
response: new Response(null, { status: 500 }),
|
|
error: "Missing tool-loop error result",
|
|
errorCode: "internal_error",
|
|
};
|
|
}
|
|
|
|
export function buildNonStreamingFinalizationPlan(
|
|
loop: ServerOwnedToolLoopResult
|
|
): NonStreamingFinalizationPlan {
|
|
const usage = loop.cumulativeUsage;
|
|
const totalCostUsd = loop.totalCostUsd;
|
|
const receiptCount = loop.receipts.length;
|
|
if (loop.kind === "error") {
|
|
return {
|
|
kind: "failure",
|
|
error: loop.errorResult ?? missingError(),
|
|
usage,
|
|
totalCostUsd,
|
|
receiptCount,
|
|
};
|
|
}
|
|
return {
|
|
kind: "success",
|
|
usage,
|
|
totalCostUsd,
|
|
receiptCount,
|
|
};
|
|
}
|
|
|
|
export async function finalizeNonStreamingRequest(
|
|
plan: NonStreamingFinalizationPlan,
|
|
deps: NonStreamingFinalizationDeps
|
|
): Promise<void> {
|
|
await deps.writeUsage(plan);
|
|
deps.writeCost(plan.totalCostUsd);
|
|
if (plan.kind === "success") {
|
|
await deps.scheduleQuota(plan);
|
|
}
|
|
deps.writeAttempt(plan);
|
|
deps.finalizePending(plan);
|
|
}
|
|
|
|
export async function finalizeToolLoopError(input: {
|
|
loop: ServerOwnedToolLoopResult;
|
|
model: string;
|
|
provider: string;
|
|
connectionId?: string;
|
|
providerRequest?: Record<string, unknown>;
|
|
persistFailureUsage: (
|
|
status: number,
|
|
errorCode: string,
|
|
usage?: FailureUsageAggregate | null
|
|
) => void;
|
|
persistAttemptLogs: (params: PersistAttemptLogsArgs) => void;
|
|
trackPendingRequest: (
|
|
model: string,
|
|
provider: string,
|
|
connectionId?: string,
|
|
isPending?: boolean
|
|
) => void;
|
|
}): Promise<ChatCoreErrorResult> {
|
|
const plan = buildNonStreamingFinalizationPlan(input.loop);
|
|
const err = plan.kind === "failure" ? plan.error : missingError();
|
|
await finalizeNonStreamingRequest(plan, {
|
|
writeUsage: () => {
|
|
input.persistFailureUsage(
|
|
err.status,
|
|
err.errorCode || `upstream_${err.status}`,
|
|
toFailureUsageAggregate(plan.usage)
|
|
);
|
|
},
|
|
writeCost: () => {},
|
|
scheduleQuota: () => {},
|
|
writeAttempt: () => {
|
|
input.persistAttemptLogs({
|
|
status: err.status,
|
|
error: err.error || "Provider request failed",
|
|
providerRequest: input.providerRequest,
|
|
clientResponse: {
|
|
error: {
|
|
message: err.error || "Provider request failed",
|
|
type: err.errorType || "api_error",
|
|
},
|
|
},
|
|
cacheSource: "upstream",
|
|
});
|
|
},
|
|
finalizePending: () => {
|
|
input.trackPendingRequest(input.model, input.provider, input.connectionId, false);
|
|
},
|
|
});
|
|
return err;
|
|
}
|