Files
OmniRoute/open-sse/handlers/chatCore/modelLifecyclePolicy.ts
MumuTW ae7a843e86 fix(autoCombo,sse): catalog hygiene — retire dead FITNESS_TABLE rows, fix 7 BUILT_IN_ALIASES targets, add model-lifecycle gate (#11503) (#11507)
Validated in a combined 4-PR batch worktree off release/v3.8.51 tip. This PR's diff overlapped taskFitness.ts and autoCombo.test.ts with the already-merged #11492/#11506 — git's merge auto-resolved both hunks cleanly (non-overlapping layers: #11492/#11506 touch layer 2 arena lookup, this PR touches layer 4 static-table hygiene); verified no conflict markers remained and re-ran the full suite after boarding.
- npm run check:model-lifecycle — PASS, 68 retired ids, 1327 catalog ids, 0 violations (re-ran with the correct `node --import tsx/esm` loader after an initial bare-node invocation mistakenly failed on path-alias resolution — that was my invocation error, not the gate)
- Focused tests: fitness-table-hygiene-11503.test.ts, taskFitness-pattern-order-8603.test.ts, model-deprecation-aliases-11503.test.ts, check-model-lifecycle-gate.test.ts, model-deprecation.test.ts, autoCombo.test.ts — part of batch's 126/126 vitest + 246/246 node:test runs
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity, check:cycles — all OK
- Full-repo lint: 228 problems remaining, all pre-existing dashboard react-hooks/* findings unrelated to this diff (zero errors in any file this PR touches)

Thanks for this — genuinely thorough methodology (segment-boundary matching, provider-scoped alias guard, offline lifecycle gate with a documented burn-down list for the 6 remaining catalog offenders).
2026-08-25 13:52:23 -03:00

63 lines
1.9 KiB
TypeScript

import { HTTP_STATUS } from "../../config/constants.ts";
import {
formatModelLifecycleMessage,
getModelLifecycleDecision,
} from "../../services/modelLifecycle.ts";
import { resolveModelAlias } from "../../services/modelDeprecation.ts";
import { createErrorResult } from "../../utils/error.ts";
type LifecycleLogger = {
info?: (tag: string, message: string) => unknown;
warn?: (tag: string, message: string) => unknown;
} | null;
function getModelLifecycleError({
provider,
model,
log,
warnOnDeprecation = false,
}: {
provider: string;
model: string;
log?: LifecycleLogger;
warnOnDeprecation?: boolean;
}): ReturnType<typeof createErrorResult> | null {
const decision = getModelLifecycleDecision(provider, model);
const message = formatModelLifecycleMessage(decision);
if (message && (decision.action === "reject" || warnOnDeprecation)) {
log?.warn?.("MODEL_LIFECYCLE", message);
}
if (decision.action !== "reject" || !message) return null;
return createErrorResult(
HTTP_STATUS.GONE,
message,
null,
"model_shutdown",
"invalid_request_error"
);
}
export function checkLifecycle(provider: string, model: string, log?: LifecycleLogger) {
return getModelLifecycleError({
provider,
// #11503: pass the provider so a globally deprecated id this provider still serves
// under its original name is left alone instead of rewritten into a 404.
model: resolveModelAlias(model, provider),
log,
});
}
export function resolveLifecycle(provider: string, model: string, log?: LifecycleLogger) {
const resolvedModel = resolveModelAlias(model, provider);
if (resolvedModel !== model) {
log?.info?.("ALIAS", `Model alias applied: ${model}${resolvedModel}`);
}
const lifecycleError = getModelLifecycleError({
provider,
model: resolvedModel,
log,
warnOnDeprecation: true,
});
return [resolvedModel, resolvedModel === model ? model : resolvedModel, lifecycleError] as const;
}