mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-02 21:32:10 +03:00
refactor(chatCore): extract resolveChatCoreRequestSetup (first setup-phase slice) (#4392)
Segundo incremento da decomposição do chatCore.ts (5127 LOC). Extrai a primeira fatia PURA da fase de request-setup do handleChatCore: a metadata de roteamento de modelo (apiFormat + targetFormat via structural-narrowing sem 'as any', + o requestedModel com fallback) — para um leaf testável em chatCore/requestSetup.ts. - resolveChatCoreRequestSetup(modelInfo, body, model): ChatCoreRequestSetup — puro, byte-idêntico ao bloco inline. Tipo ChatCoreRequestSetup é a semente do futuro ChatCoreContext (as fases maiores — setup/dispatch/streaming — virão depois). - chatCore.ts: bloco inline (~21 ln) vira 5 linhas. - 5 testes de caracterização (narrowing + fallback do requestedModel). 185/185 chatcore tests (caracterização do handleChatCore preservada), typecheck 0, complexity OK 1896 (mudança neutra), file-size encolhe. Timing: feito como leaf PURO seguro porque a v3.8.30 está finalizando — o ChatCoreContext mutável (carrega body + 20 inputs no hot-path) cabe melhor em v3.8.31. Follow-up: atribuir requestSetup.ts a um batch de mutação no nightly (como os demais chatCore leaves); 5 testes cobrem as branches por ora.
This commit is contained in:
committed by
GitHub
parent
eb48e4d953
commit
73092146e8
@@ -1,4 +1,5 @@
|
||||
import { injectMemoryAndSkills } from "./chatCore/memorySkillsInjection.ts";
|
||||
import { resolveChatCoreRequestSetup } from "./chatCore/requestSetup.ts";
|
||||
import { checkIdempotencyCache } from "./chatCore/idempotency.ts";
|
||||
import { checkSemanticCache } from "./chatCore/semanticCache.ts";
|
||||
import { sanitizeChatRequestBody } from "./chatCore/sanitization.ts";
|
||||
@@ -649,27 +650,12 @@ export async function handleChatCore({
|
||||
/* memoryUsage() never throws */
|
||||
}
|
||||
|
||||
// apiFormat is an optional custom-model marker injected by getModelInfo for
|
||||
// providers whose models can route to /chat/completions or /responses
|
||||
// (Azure AI Foundry, OCI generic OpenAI). It's not on the base ModelInfo
|
||||
// shape, so we read it via a structural narrowing without `as any`.
|
||||
const apiFormat: string | undefined =
|
||||
modelInfo && typeof modelInfo === "object" && "apiFormat" in modelInfo
|
||||
? typeof (modelInfo as { apiFormat?: unknown }).apiFormat === "string"
|
||||
? ((modelInfo as { apiFormat?: string }).apiFormat as string)
|
||||
: undefined
|
||||
: undefined;
|
||||
// #2905: per-model wire-format override for custom models, injected by
|
||||
// getModelInfo. Custom models are not in the static registry, so
|
||||
// getModelTargetFormat() can't see this — use it before the provider default.
|
||||
const customModelTargetFormat: string | undefined =
|
||||
modelInfo && typeof modelInfo === "object" && "targetFormat" in modelInfo
|
||||
? typeof (modelInfo as { targetFormat?: unknown }).targetFormat === "string"
|
||||
? ((modelInfo as { targetFormat?: string }).targetFormat as string)
|
||||
: undefined
|
||||
: undefined;
|
||||
const requestedModel =
|
||||
typeof body?.model === "string" && body.model.trim().length > 0 ? body.model : model;
|
||||
// Per-request model-routing metadata (first extracted slice of the request-setup phase).
|
||||
const { apiFormat, customModelTargetFormat, requestedModel } = resolveChatCoreRequestSetup(
|
||||
modelInfo,
|
||||
body,
|
||||
model
|
||||
);
|
||||
const isModelScope = () => isModelScopeProvider(provider, credentials?.providerSpecificData);
|
||||
const startTime = Date.now();
|
||||
// Per-request trace id + checkpoint helper. Lets us see exactly which await
|
||||
|
||||
51
open-sse/handlers/chatCore/requestSetup.ts
Normal file
51
open-sse/handlers/chatCore/requestSetup.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* chatCore request-setup resolvers (Quality Gate v2 / Fase 9 — chatCore god-file decomposition).
|
||||
*
|
||||
* The first pure slice of handleChatCore's request-setup phase: the per-request model-routing
|
||||
* metadata resolved at the very top of the handler. Side-effect-free; future increments grow this
|
||||
* into the full ChatCoreContext carrier (setup / dispatch / streaming phases).
|
||||
*/
|
||||
|
||||
export type ChatCoreRequestSetup = {
|
||||
/**
|
||||
* Optional custom-model wire-format marker injected by getModelInfo for providers whose models
|
||||
* can route to /chat/completions or /responses (Azure AI Foundry, OCI generic OpenAI). Not on
|
||||
* the base ModelInfo shape, so read via structural narrowing (never `as any`).
|
||||
*/
|
||||
apiFormat: string | undefined;
|
||||
/**
|
||||
* #2905: per-model wire-format override for custom models, injected by getModelInfo. Custom
|
||||
* models aren't in the static registry, so getModelTargetFormat() can't see this — used before
|
||||
* the provider default.
|
||||
*/
|
||||
customModelTargetFormat: string | undefined;
|
||||
/** The client-requested model string, falling back to the resolved model id when absent/blank. */
|
||||
requestedModel: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve the per-request model-routing metadata at the top of handleChatCore. Pure: a function of
|
||||
* the injected modelInfo, the request body, and the resolved model id. Behaviour is byte-identical
|
||||
* to the previous inline code.
|
||||
*/
|
||||
export function resolveChatCoreRequestSetup(
|
||||
modelInfo: unknown,
|
||||
body: { model?: unknown } | null | undefined,
|
||||
model: string
|
||||
): ChatCoreRequestSetup {
|
||||
const apiFormat: string | undefined =
|
||||
modelInfo && typeof modelInfo === "object" && "apiFormat" in modelInfo
|
||||
? typeof (modelInfo as { apiFormat?: unknown }).apiFormat === "string"
|
||||
? ((modelInfo as { apiFormat?: string }).apiFormat as string)
|
||||
: undefined
|
||||
: undefined;
|
||||
const customModelTargetFormat: string | undefined =
|
||||
modelInfo && typeof modelInfo === "object" && "targetFormat" in modelInfo
|
||||
? typeof (modelInfo as { targetFormat?: unknown }).targetFormat === "string"
|
||||
? ((modelInfo as { targetFormat?: string }).targetFormat as string)
|
||||
: undefined
|
||||
: undefined;
|
||||
const requestedModel =
|
||||
typeof body?.model === "string" && body.model.trim().length > 0 ? body.model : model;
|
||||
return { apiFormat, customModelTargetFormat, requestedModel };
|
||||
}
|
||||
40
tests/unit/chatcore-request-setup.test.ts
Normal file
40
tests/unit/chatcore-request-setup.test.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
// tests/unit/chatcore-request-setup.test.ts
|
||||
// Characterization of resolveChatCoreRequestSetup — the first extracted pure slice of
|
||||
// chatCore's request-setup phase (god-file decomposition). Locks the structural-narrowing
|
||||
// of apiFormat/targetFormat and the requestedModel fallback.
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { resolveChatCoreRequestSetup } from "../../open-sse/handlers/chatCore/requestSetup.ts";
|
||||
|
||||
test("reads apiFormat / targetFormat only when present as strings", () => {
|
||||
const r = resolveChatCoreRequestSetup(
|
||||
{ apiFormat: "responses", targetFormat: "openai" },
|
||||
{ model: "gpt-4o" },
|
||||
"resolved-model"
|
||||
);
|
||||
assert.equal(r.apiFormat, "responses");
|
||||
assert.equal(r.customModelTargetFormat, "openai");
|
||||
});
|
||||
|
||||
test("apiFormat / targetFormat absent or non-string → undefined", () => {
|
||||
assert.equal(resolveChatCoreRequestSetup({}, {}, "m").apiFormat, undefined);
|
||||
assert.equal(resolveChatCoreRequestSetup({ apiFormat: 123 }, {}, "m").apiFormat, undefined);
|
||||
assert.equal(resolveChatCoreRequestSetup({ targetFormat: null }, {}, "m").customModelTargetFormat, undefined);
|
||||
});
|
||||
|
||||
test("modelInfo that is not an object → both markers undefined", () => {
|
||||
const r = resolveChatCoreRequestSetup(null, { model: "x" }, "m");
|
||||
assert.equal(r.apiFormat, undefined);
|
||||
assert.equal(r.customModelTargetFormat, undefined);
|
||||
});
|
||||
|
||||
test("requestedModel uses the client body.model when it is a non-blank string", () => {
|
||||
assert.equal(resolveChatCoreRequestSetup({}, { model: "claude-3" }, "fallback").requestedModel, "claude-3");
|
||||
});
|
||||
|
||||
test("requestedModel falls back to the resolved model id when body.model is blank/absent/non-string", () => {
|
||||
assert.equal(resolveChatCoreRequestSetup({}, { model: " " }, "fallback").requestedModel, "fallback");
|
||||
assert.equal(resolveChatCoreRequestSetup({}, {}, "fallback").requestedModel, "fallback");
|
||||
assert.equal(resolveChatCoreRequestSetup({}, { model: 42 }, "fallback").requestedModel, "fallback");
|
||||
assert.equal(resolveChatCoreRequestSetup({}, null, "fallback").requestedModel, "fallback");
|
||||
});
|
||||
Reference in New Issue
Block a user