feat(combo): add nestedComboMode execute — black-box combo-ref execution for selection strategies (#4537)

Integrated into release/v3.8.33
This commit is contained in:
Abhishek Divekar
2026-06-22 04:21:22 +05:30
committed by GitHub
parent 548f1fe56f
commit 09d8a4aa8c
13 changed files with 669 additions and 21 deletions

View File

@@ -1,6 +1,7 @@
{
"_comment": "Catraca de complexidade (check-complexity.mjs, ESLint core rules complexity>=15 e max-lines-per-function>80 sobre src+open-sse+electron+bin via eslint.complexity.config.mjs). Conta total de violacoes; so pode cair. --update ratcheta.",
"count": 1913,
"count": 1915,
"_rebaseline_2026_06_21_v3833_4537_nested_combo": "Reconciliacao 1913->1915 (+2) do PR #4537 (nestedComboMode execute — black-box combo-ref execution). O +2 vem do branch novo de dispatch nested em handleComboChat (combo.ts: normalizeNestedComboMode + executeModeUnits/hasExecutableComboRef + o ramo simpleExecuteStrategies que roda resolveComboRuntimeUnits com recursion caps depth/cycle/budget) — ramos condicionais no chokepoint de dispatch do combo. Medido com `node scripts/check/check-complexity.mjs` no estado merged. Crescimento de feature legitimo recem-TDD'd, nao regressao; reducao estrutural fica como debt (#3501).",
"_rebaseline_2026_06_21_v3833_reviewprs_r4_owner": "Reconciliacao release-volatil 1911->1913 (+2) apos o lote de PRs do owner desta rodada (#4560 RTK cache_control, #4552 Cursor auto-import macOS, #4551 Codex /responses probe, #4554 Cursor Composer </think> decode + o stack #3501 #4538/#4544/#4548). O fast-path do release nao roda check:complexity (so release->main). As 3 extracoes chatCore #3501 (#4538 recupera 4 leaves, #4544 failureUsage, #4548 claudeSystemRole+upstreamExecuteHeaders) sao PURAS/complexity-neutras (movem codigo p/ leaves sob o teto, chatCore ENCOLHE); o +2 vem dos condicionais NOVOS das features .32-portadas (#4554 decode de bloco </think> visivel do Composer + #4551 probe do endpoint real Codex /responses). Medido com `node scripts/check/check-complexity.mjs` no tip 4b34a75fe. Crescimento de feature legitimo recem-TDD'd, nao regressao; reducao estrutural fica como debt (#3501).",
"_rebaseline_2026_06_21_v3833_reviewprs_r4": "Reconciliacao release-volatil 1906->1911 (+5) apos o lote /review-prs r4 (5 PRs de contribuidores mergeados em release/v3.8.33: #4557 health-polls, #4556 mobile-table, #4545 provider-wildcard, #4558 isHidden, #4489 sticky-weighted) + 1 merge concorrente de sessao paralela (#4565 quota perf). O fast-path do release nao roda check:complexity (so release->main), entao o ramo acumula sem rebaselinar. Condicionais NOVOS legitimos: #4545 (expandProviderWildcardsInCombo/Collection + wildcardMatch glob/registry branching em providerWildcard.ts/wildcardRouter.ts), #4489 (eligibility pass sticky-weighted: resolveWeightedStepGroups + isTargetSelectableForWeighted + renormalizacao em combo.ts), #4558 (filtro isHidden em buildAutoCandidates/virtualFactory) e #4565 (guardas de skip de quota_snapshots idle). Medido com `node scripts/check/check-complexity.mjs` no tip 39b2bbfea. Mesma familia dos rebaselines anteriores — crescimento de feature legitimo recem-TDD'd, nao regressao; reducao estrutural fica como debt (#3501).",
"_rebaseline_2026_06_21_v3833_cycle_open_stranded": "1905 -> 1906 (+1). Abertura do ciclo v3.8.33: o +1 vem dos 4 commits stranded na release/v3.8.32 (pós-merge da v3.8.32) trazidos via cherry-pick para release/v3.8.33 — isolado em #4483 (autoStrategy.ts evaluateQuotaCutoff/quotaPreflight.ts, guardas de quota-cutoff inerentemente ramificadas, opt-in default-OFF). main mede 1905; o .33 branch (main + cherry-picks) mede 1906. Crescimento de feature legitimo recém-mergeado, não regressão; redução fica como debt (#3501).",

View File

@@ -130,7 +130,7 @@
"open-sse/services/batchProcessor.ts": 828,
"open-sse/services/browserBackedChat.ts": 850,
"open-sse/services/claudeCodeCompatible.ts": 1202,
"open-sse/services/combo.ts": 2818,
"open-sse/services/combo.ts": 2955,
"open-sse/services/rateLimitManager.ts": 1035,
"open-sse/services/tokenRefresh.ts": 1997,
"open-sse/services/usage.ts": 3450,
@@ -145,7 +145,7 @@
"src/app/(dashboard)/dashboard/cache/page.tsx": 841,
"src/app/(dashboard)/dashboard/cli-code/components/CodexToolCard.tsx": 900,
"src/app/(dashboard)/dashboard/cloud-agents/page.tsx": 922,
"src/app/(dashboard)/dashboard/combos/page.tsx": 4406,
"src/app/(dashboard)/dashboard/combos/page.tsx": 4434,
"src/app/(dashboard)/dashboard/costs/CostOverviewTab.tsx": 1495,
"src/app/(dashboard)/dashboard/costs/quota-share/components/PoolWizard.tsx": 1007,
"src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx": 2570,
@@ -205,7 +205,7 @@
"src/shared/constants/sidebarVisibility.ts": 1100,
"src/shared/services/cliRuntime.ts": 1090,
"src/shared/validation/schemas.ts": 2523,
"src/sse/handlers/chat.ts": 1513,
"src/sse/handlers/chat.ts": 1515,
"src/sse/services/auth.ts": 2279
},
"testCap": 800,
@@ -221,6 +221,7 @@
"tests/unit/chatcore-translation-paths.test.ts": 2810,
"tests/unit/chatgpt-web.test.ts": 2809,
"tests/unit/combo-routing-engine.test.ts": 3213,
"tests/unit/combo-strategy-fallbacks.test.ts": 880,
"tests/unit/db-core-init.test.ts": 864,
"tests/unit/db-migration-runner.test.ts": 1491,
"tests/unit/db-settings-crud.test.ts": 941,

View File

@@ -97,7 +97,9 @@ import type {
SingleModelTarget,
HandleComboChatOptions,
HandleRoundRobinOptions,
NestedComboMode,
ResolvedComboTarget,
ResolvedComboUnit,
AutoProviderCandidate,
ComboRuntimeStep,
HistoricalLatencyStatsEntry,
@@ -132,6 +134,7 @@ import {
getExhaustedTargetSkipReason,
} from "./combo/comboPredicates.ts";
import { applyComboTargetExhaustion } from "./combo/targetExhaustion.ts";
import { executeRuntimeUnitCombo } from "./combo/runtimeUnits.ts";
import { dedupeTargetsByExecutionKey, isRecord } from "./combo/comboData.ts";
import {
expandProviderWildcardsInCombo,
@@ -146,6 +149,7 @@ import {
import {
filterTargetsByRequestCompatibility,
getModelContextLimitForModelString,
resolveComboRuntimeUnits,
resolveComboTargets,
resolveWeightedTargets,
resolveWeightedStepGroups,
@@ -191,7 +195,7 @@ export { resolveShadowTargets, scheduleShadowRouting };
// preScreenTargets was public from combo.ts before the reset-aware quota
// extraction (combo split D7b). Keep the external surface stable.
export { preScreenTargets };
export { resolveComboTargets, filterTargetsByRequestCompatibility };
export { resolveComboRuntimeUnits, resolveComboTargets, filterTargetsByRequestCompatibility };
export {
getComboFromData,
getComboModelsFromData,
@@ -225,6 +229,10 @@ const MIN_HISTORY_SAMPLES = 10;
// Matches the example in GitHub issue #1812 (e.g. o3-like model: $3 input/$15 output).
const OUTPUT_TOKEN_RATIO = 0.4;
function normalizeNestedComboMode(value: unknown): NestedComboMode {
return value === "execute" ? "execute" : "flatten";
}
function calculateTargetContextAffinity(
target: ResolvedComboTarget,
sessionId: string | null | undefined
@@ -571,6 +579,7 @@ export async function handleComboChat({
relayOptions,
signal,
apiKeyAllowedConnections = null,
nesting = null,
}: HandleComboChatOptions): Promise<Response> {
// Combo setup phase (god-file decomposition fase 1): strategy / relay / resilience /
// universal-handoff / context-cache pinning / agent middleware / config cascade / timeout.
@@ -683,6 +692,132 @@ export async function handleComboChat({
return handleSingleModelWithTimeout(body, pinnedModel);
}
const nestingContext = nesting || {
depth: 0,
maxDepth: clampComboDepth(config.maxComboDepth),
visitedComboNames: [combo.name],
rootComboName: combo.name,
attemptBudget: { count: 0, limit: MAX_GLOBAL_ATTEMPTS },
};
const nestedComboMode = normalizeNestedComboMode(config.nestedComboMode);
const executeModeUnits =
nestedComboMode === "execute" && allCombos
? resolveComboRuntimeUnits(combo, allCombos, "execute", nestingContext.maxDepth)
: [];
const hasExecutableComboRef = executeModeUnits.some((unit) => unit.kind === "combo-ref");
const simpleExecuteStrategies = new Set([
"priority",
"round-robin",
"random",
"strict-random",
"weighted",
"fill-first",
]);
if (hasExecutableComboRef && simpleExecuteStrategies.has(strategy)) {
let runtimeUnits = executeModeUnits;
let unitExecutionStrategy = strategy;
if (strategy === "weighted") {
const stickyLimit = clampStickyWeightedTargetLimit(
(config as Record<string, unknown>).stickyWeightedLimit
);
const stickyKey = getStickyWeightedExecutionKey(combo.name, stickyLimit);
const stickyUnit = stickyKey
? runtimeUnits.find((unit) => unit.executionKey === stickyKey)
: null;
if (stickyUnit) {
runtimeUnits = [
stickyUnit,
...runtimeUnits.filter((unit) => unit.executionKey !== stickyUnit.executionKey),
];
unitExecutionStrategy = "priority";
}
}
if (strategy === "random") runtimeUnits = fisherYatesShuffle([...runtimeUnits]);
if (strategy === "strict-random") {
const key = await getNextFromDeck(
`combo:${combo.name}`,
runtimeUnits.map((unit) => unit.executionKey)
);
const selected = runtimeUnits.find((unit) => unit.executionKey === key) || runtimeUnits[0];
runtimeUnits = [
selected,
...runtimeUnits.filter((unit) => unit.executionKey !== selected.executionKey),
];
}
let runtimeStickyLimit: number | null = null;
let runtimeStickyTargets: ResolvedComboUnit[] = runtimeUnits;
if (strategy === "round-robin") {
const perComboStickyLimit = (config as Record<string, unknown>).stickyRoundRobinLimit;
runtimeStickyLimit = clampStickyRoundRobinTargetLimit(
perComboStickyLimit !== undefined && perComboStickyLimit !== null
? perComboStickyLimit
: (settings as Record<string, unknown> | null)?.stickyRoundRobinLimit
);
const { startIndex, counter } = getStickyRoundRobinStartIndex(
combo.name,
runtimeUnits,
runtimeStickyLimit
);
if (runtimeStickyLimit <= 1) rrCounters.set(combo.name, counter + 1);
runtimeUnits = runtimeUnits.map(
(_, offset) => runtimeUnits[(startIndex + offset) % runtimeUnits.length]
);
runtimeStickyTargets = executeModeUnits;
}
const execution = await executeRuntimeUnitCombo({
body,
combo,
strategy: unitExecutionStrategy,
effectiveComboStrategy: strategy,
units: runtimeUnits,
handleSingleModel: handleSingleModelWithTimeout,
isModelAvailable,
log,
config,
settings,
allCombos,
signal,
nesting: nestingContext,
baseOptions: {
body,
combo,
handleSingleModel,
isModelAvailable,
log,
settings,
allCombos,
relayOptions,
signal,
apiKeyAllowedConnections,
},
runCombo: handleComboChat,
});
if (strategy === "weighted" && execution.response.ok && execution.unit) {
const stickyLimit = clampStickyWeightedTargetLimit(
(config as Record<string, unknown>).stickyWeightedLimit
);
if (stickyLimit > 1)
recordStickyWeightedSuccess(combo.name, execution.unit.executionKey, stickyLimit);
}
if (
strategy === "round-robin" &&
execution.response.ok &&
execution.unit &&
runtimeStickyLimit &&
runtimeStickyLimit > 1
) {
recordStickyRoundRobinSuccess(
combo.name,
execution.unit,
runtimeStickyLimit,
runtimeStickyTargets
);
}
return execution.response;
}
// Route to round-robin handler if strategy matches
if (strategy === "round-robin") {
return handleRoundRobinCombo({
@@ -1549,6 +1684,7 @@ export async function handleComboChat({
}
const result = await handleSingleModelWithTimeout(attemptBody, modelStr, {
...targetForAttempt,
effectiveComboStrategy: strategy,
failoverBeforeRetry: config.failoverBeforeRetry,
});
@@ -2499,6 +2635,7 @@ async function handleRoundRobinCombo({
const result = await handleSingleModel(attemptBody, modelStr, {
...targetForAttempt,
effectiveComboStrategy: "round-robin",
failoverBeforeRetry: config.failoverBeforeRetry,
});

View File

@@ -29,7 +29,9 @@ import type {
ComboLike,
ComboLogger,
ComboRuntimeStep,
NestedComboMode,
ResolvedComboTarget,
ResolvedComboUnit,
} from "./types.ts";
function toTrimmedString(value: unknown): string | null {
@@ -607,6 +609,17 @@ export function resolveComboTargets(
: getDirectComboTargets(combo);
}
export function resolveComboRuntimeUnits(
combo: ComboLike,
allCombos: ComboCollectionLike,
mode: NestedComboMode,
maxDepth: number = MAX_COMBO_DEPTH
): ResolvedComboUnit[] {
if (mode === "flatten" || !allCombos) return resolveComboTargets(combo, allCombos, maxDepth);
validateComboDAG(combo.name, allCombos, new Set<string>(), 0, maxDepth);
return getOrderedTopLevelRuntimeSteps(combo, allCombos);
}
export function resolveWeightedStepGroups(
combo: ComboLike,
allCombos: ComboCollectionLike

View File

@@ -13,7 +13,7 @@
* Pure leaf: this module never imports from the combo barrel.
*/
import type { ResolvedComboTarget } from "./types.ts";
import type { ResolvedComboUnit } from "./types.ts";
// In-memory atomic counter per combo for round-robin distribution
// Resets on server restart (by design — no stale state)
@@ -37,7 +37,7 @@ export const clampStickyWeightedTargetLimit = clampStickyRoundRobinTargetLimit;
export function getStickyRoundRobinStartIndex(
comboName: string,
targets: ResolvedComboTarget[],
targets: ResolvedComboUnit[],
stickyLimit: number
): { startIndex: number; counter: number } {
const sticky = rrStickyTargets.get(comboName);
@@ -54,9 +54,9 @@ export function getStickyRoundRobinStartIndex(
export function recordStickyRoundRobinSuccess(
comboName: string,
target: ResolvedComboTarget,
target: ResolvedComboUnit,
stickyLimit: number,
targets: ResolvedComboTarget[]
targets: ResolvedComboUnit[]
): void {
const sticky = rrStickyTargets.get(comboName);
const successCount = sticky?.executionKey === target.executionKey ? sticky.successCount + 1 : 1;

View File

@@ -0,0 +1,256 @@
// Nested combo runtime unit execution — see combo.ts for integration.
import { errorResponse } from "../../utils/error.ts";
import { recordComboRequest } from "../comboMetrics.ts";
import { resolveDelayMs } from "./comboPredicates.ts";
import { validateResponseQuality } from "./validateQuality.ts";
import type {
ComboCollectionLike,
ComboLike,
ComboLogger,
ComboNestingContext,
HandleComboChatOptions,
HandleSingleModel,
IsModelAvailable,
ResolvedComboRefTarget,
ResolvedComboUnit,
} from "./types.ts";
export type RuntimeUnitExecutionResult = {
response: Response;
unit: ResolvedComboUnit | null;
};
type RuntimeUnitRunner = (options: HandleComboChatOptions) => Promise<Response>;
function isRecord(value: unknown): value is Record<string, unknown> {
return !!value && typeof value === "object" && !Array.isArray(value);
}
function getCombosList(allCombos: ComboCollectionLike): ComboLike[] {
const combos = Array.isArray(allCombos) ? allCombos : allCombos?.combos || [];
return combos.filter(
(combo): combo is ComboLike => isRecord(combo) && typeof combo.name === "string"
);
}
function findComboByName(allCombos: ComboCollectionLike, name: string): ComboLike | null {
return getCombosList(allCombos).find((combo) => combo.name === name) || null;
}
function unitDisplayName(unit: ResolvedComboUnit): string {
return unit.kind === "combo-ref" ? `combo:${unit.comboName}` : unit.modelStr;
}
function shuffleUnits(units: ResolvedComboUnit[]): ResolvedComboUnit[] {
const result = [...units];
for (let index = result.length - 1; index > 0; index -= 1) {
const swapIndex = Math.floor(Math.random() * (index + 1));
[result[index], result[swapIndex]] = [result[swapIndex], result[index]];
}
return result;
}
function selectWeightedUnit(units: ResolvedComboUnit[]): ResolvedComboUnit | null {
const total = units.reduce((sum, unit) => sum + Math.max(0, Number(unit.weight) || 0), 0);
if (total <= 0) return units[0] || null;
let draw = Math.random() * total;
for (const unit of units) {
draw -= Math.max(0, Number(unit.weight) || 0);
if (draw <= 0) return unit;
}
return units[units.length - 1] || null;
}
async function executeModelUnit(args: {
body: Record<string, unknown>;
unit: Extract<ResolvedComboUnit, { kind: "model" }>;
handleSingleModel: HandleSingleModel;
isModelAvailable?: IsModelAvailable;
failoverBeforeRetry: unknown;
effectiveComboStrategy: string;
}): Promise<Response> {
if (args.isModelAvailable) {
const available = await args.isModelAvailable(args.unit.modelStr, args.unit);
if (!available) return errorResponse(503, `Model ${args.unit.modelStr} is unavailable`);
}
return args.handleSingleModel(args.body, args.unit.modelStr, {
...args.unit,
effectiveComboStrategy: args.effectiveComboStrategy,
failoverBeforeRetry: args.failoverBeforeRetry,
});
}
function buildChildNestingContext(args: {
context: ComboNestingContext;
childComboName: string;
}): ComboNestingContext | Response {
if (args.context.depth >= args.context.maxDepth) {
return errorResponse(503, `Max combo nesting depth (${args.context.maxDepth}) exceeded`);
}
if (args.context.visitedComboNames.includes(args.childComboName)) {
return errorResponse(503, `Circular combo reference detected: ${args.childComboName}`);
}
return {
...args.context,
depth: args.context.depth + 1,
visitedComboNames: [...args.context.visitedComboNames, args.childComboName],
};
}
async function executeComboRefUnit(args: {
body: Record<string, unknown>;
unit: ResolvedComboRefTarget;
allCombos: ComboCollectionLike;
runCombo: RuntimeUnitRunner;
baseOptions: HandleComboChatOptions;
nesting: ComboNestingContext;
}): Promise<Response> {
const childCombo = findComboByName(args.allCombos, args.unit.comboName);
if (!childCombo) return errorResponse(503, `Nested combo "${args.unit.comboName}" not found`);
const childNesting = buildChildNestingContext({
context: args.nesting,
childComboName: childCombo.name,
});
if (childNesting instanceof Response) return childNesting;
return args.runCombo({
...args.baseOptions,
body: args.body,
combo: childCombo,
nesting: childNesting,
});
}
async function executeRuntimeUnit(args: {
body: Record<string, unknown>;
unit: ResolvedComboUnit;
allCombos: ComboCollectionLike;
handleSingleModel: HandleSingleModel;
isModelAvailable?: IsModelAvailable;
runCombo: RuntimeUnitRunner;
baseOptions: HandleComboChatOptions;
nesting: ComboNestingContext;
failoverBeforeRetry: unknown;
effectiveComboStrategy: string;
}): Promise<Response> {
if (args.unit.kind === "model") {
return executeModelUnit({
body: args.body,
unit: args.unit,
handleSingleModel: args.handleSingleModel,
isModelAvailable: args.isModelAvailable,
failoverBeforeRetry: args.failoverBeforeRetry,
effectiveComboStrategy: args.effectiveComboStrategy,
});
}
return executeComboRefUnit({
body: args.body,
unit: args.unit,
allCombos: args.allCombos,
runCombo: args.runCombo,
baseOptions: args.baseOptions,
nesting: args.nesting,
});
}
function orderUnitsForStrategy(strategy: string, units: ResolvedComboUnit[]): ResolvedComboUnit[] {
if (strategy === "random") return shuffleUnits(units);
if (strategy === "weighted") {
const selected = selectWeightedUnit(units);
if (!selected) return units;
return [selected, ...units.filter((unit) => unit.executionKey !== selected.executionKey)];
}
return units;
}
export async function executeRuntimeUnitCombo(args: {
body: Record<string, unknown>;
combo: ComboLike;
strategy: string;
effectiveComboStrategy?: string;
units: ResolvedComboUnit[];
handleSingleModel: HandleSingleModel;
isModelAvailable?: IsModelAvailable;
log: ComboLogger;
config: Record<string, unknown>;
settings?: Record<string, unknown> | null;
allCombos: ComboCollectionLike;
signal?: AbortSignal | null;
nesting: ComboNestingContext;
baseOptions: HandleComboChatOptions;
runCombo: RuntimeUnitRunner;
}): Promise<RuntimeUnitExecutionResult> {
const maxRetries = Number(args.config.maxRetries ?? 1);
const retryDelayMs = resolveDelayMs(args.config.retryDelayMs, 2000);
const orderedUnits = orderUnitsForStrategy(args.strategy, args.units);
const clientRequestedStream = args.body?.stream === true;
const startTime = Date.now();
const effectiveStrategy = args.effectiveComboStrategy ?? args.strategy;
let lastResponse: Response | null = null;
let fallbackCount = 0;
for (const unit of orderedUnits) {
for (let retry = 0; retry <= maxRetries; retry += 1) {
if (args.signal?.aborted)
return { response: errorResponse(499, "Client disconnected"), unit };
args.nesting.attemptBudget.count += 1;
if (args.nesting.attemptBudget.count > args.nesting.attemptBudget.limit) {
return { response: errorResponse(503, "Maximum combo retry limit reached"), unit };
}
if (retry > 0) {
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
}
args.log.info(
"COMBO",
`Trying ${unit.kind} ${unitDisplayName(unit)}${retry > 0 ? ` (retry ${retry})` : ""}`
);
const response = await executeRuntimeUnit({
body: args.body,
unit,
allCombos: args.allCombos,
handleSingleModel: args.handleSingleModel,
isModelAvailable: args.isModelAvailable,
runCombo: args.runCombo,
baseOptions: args.baseOptions,
nesting: args.nesting,
failoverBeforeRetry: args.config.failoverBeforeRetry,
effectiveComboStrategy: effectiveStrategy,
});
lastResponse = response;
if (response.ok) {
if (unit.kind === "combo-ref") {
recordComboRequest(args.combo.name, null, {
success: true,
latencyMs: Date.now() - startTime,
fallbackCount,
strategy: effectiveStrategy,
target: { executionKey: unit.executionKey, stepId: unit.stepId, label: unit.label },
});
return { response, unit };
}
const quality = await validateResponseQuality(response, clientRequestedStream, args.log);
if (quality.valid) {
recordComboRequest(args.combo.name, unit.modelStr, {
success: true,
latencyMs: Date.now() - startTime,
fallbackCount,
strategy: effectiveStrategy,
target: { executionKey: unit.executionKey, stepId: unit.stepId, label: unit.label },
});
return { response: quality.clonedResponse ?? response, unit };
}
}
if (![408, 429, 500, 502, 503, 504].includes(response.status)) break;
}
fallbackCount += 1;
}
recordComboRequest(args.combo.name, null, {
success: false,
latencyMs: Date.now() - startTime,
fallbackCount,
strategy: effectiveStrategy,
});
return {
response: lastResponse || errorResponse(503, "All nested combo units unavailable"),
unit: null,
};
}

View File

@@ -44,6 +44,7 @@ export type ComboLogger = {
export type SingleModelTarget =
| (ResolvedComboTarget & {
allowRateLimitedConnection?: boolean;
effectiveComboStrategy?: string | null;
modelAbortSignal?: AbortSignal | null;
})
| { modelAbortSignal: AbortSignal };
@@ -65,6 +66,16 @@ export type ComboRelayOptions = {
[key: string]: unknown;
};
export type NestedComboMode = "flatten" | "execute";
export type ComboNestingContext = {
depth: number;
maxDepth: number;
visitedComboNames: string[];
rootComboName: string;
attemptBudget: { count: number; limit: number };
};
export type HandleComboChatOptions = {
body: Record<string, unknown>;
combo: ComboLike;
@@ -76,6 +87,7 @@ export type HandleComboChatOptions = {
relayOptions?: ComboRelayOptions | null;
signal?: AbortSignal | null;
apiKeyAllowedConnections?: string[] | null;
nesting?: ComboNestingContext | null;
};
export type HandleRoundRobinOptions = Omit<
@@ -130,13 +142,15 @@ export type ShadowRoutingConfig = {
timeoutMs: number;
};
export type ComboRuntimeStep =
| ResolvedComboTarget
| {
kind: "combo-ref";
stepId: string;
executionKey: string;
comboName: string;
weight: number;
label: string | null;
};
export type ResolvedComboRefTarget = {
kind: "combo-ref";
stepId: string;
executionKey: string;
comboName: string;
weight: number;
label: string | null;
};
export type ResolvedComboUnit = ResolvedComboTarget | ResolvedComboRefTarget;
export type ComboRuntimeStep = ResolvedComboUnit;

View File

@@ -50,6 +50,7 @@ const DEFAULT_COMBO_CONFIG = {
handoffProviders: ["codex"],
maxMessagesForSummary: 30,
maxComboDepth: 3,
nestedComboMode: "flatten",
trackMetrics: true,
reasoningTokenBufferEnabled: true,
manifestRouting: false,

View File

@@ -169,6 +169,8 @@ const ADVANCED_FIELD_HELP_FALLBACK = {
"Number of times to retry the full target set when every target fails. 0 = no set-level retry.",
setRetryDelayMs:
"Delay between set-level retry attempts, giving transient issues time to resolve.",
nestedComboMode:
"How references to other combos are handled. Flatten expands a combo ref into this combo's target list (legacy). Execute treats a combo ref as a black-box target: the parent strategy selects the child combo, then the child runs its own strategy and retries.",
};
const LEGACY_COMBO_RESILIENCE_KEYS = new Set([
@@ -3880,6 +3882,32 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders, combo
</div>
</div>
)}
<div className="grid grid-cols-1 gap-2 pt-2 border-t border-black/5 dark:border-white/5">
<div>
<FieldLabelWithHelp
label={getI18nOrFallback(t, "nestedComboMode", "Nested Combo Behavior")}
help={getI18nOrFallback(
t,
"advancedHelp.nestedComboMode",
ADVANCED_FIELD_HELP_FALLBACK.nestedComboMode
)}
showHelp={!isExpertMode}
/>
<select
value={config.nestedComboMode ?? "flatten"}
onChange={(e) =>
setConfig({
...config,
nestedComboMode: e.target.value === "execute" ? "execute" : "flatten",
})
}
className="w-full text-xs py-1.5 px-2 rounded border border-black/10 dark:border-white/10 bg-surface-1 focus:border-primary focus:outline-none"
>
<option value="flatten">Flatten nested combos</option>
<option value="execute">Execute nested combos as targets</option>
</select>
</div>
</div>
{strategy === "context-relay" && (
<div className="grid grid-cols-1 md:grid-cols-3 gap-2 pt-2 border-t border-black/5 dark:border-white/5">
<div>

View File

@@ -146,6 +146,7 @@ export const comboRuntimeConfigSchema = z
handoffProviders: z.array(z.string().trim().min(1).max(100)).max(10).optional(),
maxMessagesForSummary: z.coerce.number().int().min(5).max(100).optional(),
maxComboDepth: z.coerce.number().int().min(1).max(10).optional(),
nestedComboMode: z.enum(["flatten", "execute"]).optional(),
trackMetrics: z.boolean().optional(),
reasoningTokenBufferEnabled: z.boolean().optional(),
compressionMode: compressionModeSchema.optional(),

View File

@@ -636,6 +636,7 @@ export async function handleChat(
allowedConnectionIds?: string[] | null;
failoverBeforeRetry?: boolean;
providerId?: string | null;
effectiveComboStrategy?: string | null;
}
) =>
handleSingleModelChat(
@@ -662,7 +663,7 @@ export async function handleChat(
cachedSettings: settings,
providerId: target?.providerId ?? null,
},
combo.strategy,
target?.effectiveComboStrategy ?? combo.strategy,
true
),
isModelAvailable: checkModelAvailable,
@@ -825,6 +826,7 @@ async function handleSingleModelChat(
failoverBeforeRetry?: boolean;
allowRateLimitedConnection?: boolean;
providerId?: string | null;
effectiveComboStrategy?: string | null;
}
) =>
handleSingleModelChat(
@@ -846,7 +848,7 @@ async function handleSingleModelChat(
allowRateLimitedConnection: target?.allowRateLimitedConnection === true,
providerId: target?.providerId ?? null,
},
redirectCombo.strategy ?? "priority",
target?.effectiveComboStrategy ?? redirectCombo.strategy ?? "priority",
false
),
isModelAvailable: async () => true,

View File

@@ -26,6 +26,7 @@ test("getDefaultComboConfig returns a fresh copy of the defaults", () => {
assert.equal(first.handoffThreshold, 0.85);
assert.equal(first.maxMessagesForSummary, 30);
assert.deepEqual(first.handoffProviders, ["codex"]);
assert.equal(first.nestedComboMode, "flatten");
assert.equal(first.failoverBeforeRetry, true);
assert.equal(first.maxSetRetries, 0);
assert.equal(first.setRetryDelayMs, 2000);
@@ -531,6 +532,30 @@ test("createComboSchema accepts failoverBeforeRetry, maxSetRetries and setRetryD
assert.equal(parsed.config.setRetryDelayMs, 1500);
});
test("createComboSchema accepts nestedComboMode and rejects invalid values", () => {
const parsed = createComboSchema.parse({
name: "nested-execute",
models: [{ kind: "combo-ref", comboName: "child" }],
strategy: "priority",
config: { nestedComboMode: "execute" },
});
assert.equal(parsed.config.nestedComboMode, "execute");
const flatten = createComboSchema.parse({
name: "nested-flatten",
models: ["openai/gpt-4o-mini"],
config: { nestedComboMode: "flatten" },
});
assert.equal(flatten.config.nestedComboMode, "flatten");
const invalid = createComboSchema.safeParse({
name: "nested-invalid",
models: ["openai/gpt-4o-mini"],
config: { nestedComboMode: "redirect" },
});
assert.equal(invalid.success, false);
});
test("createComboSchema accepts per-combo stickyRoundRobinLimit and rejects out-of-range", () => {
const parsed = createComboSchema.parse({
name: "sticky-override",
@@ -618,6 +643,17 @@ test("createComboSchema rejects setRetryDelayMs out of range", () => {
assert.equal(negative.success, false);
});
test("resolveComboConfig cascades nestedComboMode", () => {
const result = resolveComboConfig(
{ config: { nestedComboMode: "execute" } },
{ comboDefaults: { nestedComboMode: "flatten" } }
);
assert.equal(result.nestedComboMode, "execute");
const defaulted = resolveComboConfig({ config: {} }, { comboDefaults: {} });
assert.equal(defaulted.nestedComboMode, "flatten");
});
test("resolveComboConfig cascades failoverBeforeRetry, maxSetRetries and setRetryDelayMs", () => {
const result = resolveComboConfig(
{

View File

@@ -600,6 +600,164 @@ test("strict-random survives a stale deck entry after a target is removed", asyn
assert.deepEqual(calls, ["claude/sonnet"]);
});
test("nested execute mode treats combo refs as black-box priority targets", async () => {
const calls: string[] = [];
const outer = {
name: "outer-execute-priority",
strategy: "priority",
models: [{ kind: "combo-ref", comboName: "child-priority" }, "gemini/fallback"],
config: { nestedComboMode: "execute", maxRetries: 0, retryDelayMs: 0 },
};
const child = {
name: "child-priority",
strategy: "priority",
models: ["openai/a", "claude/b"],
config: { maxRetries: 0, retryDelayMs: 0 },
};
const result = await handleComboChat({
body: {},
combo: outer,
handleSingleModel: async (_body: any, modelStr: string) => {
calls.push(modelStr);
return okResponse();
},
isModelAvailable: async () => true,
log: createLog(),
settings: null,
allCombos: [outer, child],
});
assert.equal(result.ok, true);
assert.deepEqual(calls, ["openai/a"]);
});
test("nested execute mode falls back to the next parent target after a child combo fails", async () => {
const calls: string[] = [];
const outer = {
name: "outer-execute-fallback",
strategy: "priority",
models: [{ kind: "combo-ref", comboName: "child-down" }, "gemini/fallback"],
config: { nestedComboMode: "execute", maxRetries: 0, retryDelayMs: 0 },
};
const child = {
name: "child-down",
strategy: "priority",
models: ["openai/a", "claude/b"],
config: { maxRetries: 0, retryDelayMs: 0 },
};
const result = await handleComboChat({
body: {},
combo: outer,
handleSingleModel: async (_body: any, modelStr: string) => {
calls.push(modelStr);
return modelStr === "gemini/fallback" ? okResponse() : errorResponse(503);
},
isModelAvailable: async () => true,
log: createLog(),
settings: null,
allCombos: [outer, child],
});
assert.equal(result.ok, true);
assert.deepEqual(calls, ["openai/a", "claude/b", "gemini/fallback"]);
});
test("nested execute mode supports 3-level nesting", async () => {
const calls: string[] = [];
const a = {
name: "level-a",
strategy: "priority",
models: [{ kind: "combo-ref", comboName: "level-b" }],
config: { nestedComboMode: "execute", maxRetries: 0 },
};
const b = {
name: "level-b",
strategy: "priority",
models: [{ kind: "combo-ref", comboName: "level-c" }],
config: { nestedComboMode: "execute", maxRetries: 0 },
};
const c = {
name: "level-c",
strategy: "priority",
models: ["openai/leaf"],
config: { maxRetries: 0 },
};
const result = await handleComboChat({
body: {},
combo: a,
handleSingleModel: async (_body: any, modelStr: string) => {
calls.push(modelStr);
return okResponse();
},
isModelAvailable: async () => true,
log: createLog(),
settings: null,
allCombos: [a, b, c],
});
assert.equal(result.ok, true);
assert.deepEqual(calls, ["openai/leaf"]);
});
test("nested execute mode fails cleanly on a circular combo reference", async () => {
const a = {
name: "cycle-a",
strategy: "priority",
models: [{ kind: "combo-ref", comboName: "cycle-b" }],
config: { nestedComboMode: "execute", maxRetries: 0 },
};
const b = {
name: "cycle-b",
strategy: "priority",
models: [{ kind: "combo-ref", comboName: "cycle-a" }],
config: { nestedComboMode: "execute", maxRetries: 0 },
};
await assert.rejects(
async () =>
handleComboChat({
body: {},
combo: a,
handleSingleModel: async () => okResponse(),
isModelAvailable: async () => true,
log: createLog(),
settings: null,
allCombos: [a, b],
}),
/[Cc]ircular/
);
});
test("flatten mode (default) expands nested combo refs into leaf targets", async () => {
const calls: string[] = [];
const outer = {
name: "outer-flatten-default",
strategy: "priority",
models: [{ kind: "combo-ref", comboName: "child-flat" }],
config: { maxRetries: 0 },
};
const child = {
name: "child-flat",
strategy: "priority",
models: ["openai/a", "claude/b"],
config: { maxRetries: 0 },
};
const result = await handleComboChat({
body: {},
combo: outer,
handleSingleModel: async (_body: any, modelStr: string) => {
calls.push(modelStr);
return modelStr === "openai/a" ? errorResponse(503) : okResponse();
},
isModelAvailable: async () => true,
log: createLog(),
settings: null,
allCombos: [outer, child],
});
assert.equal(result.ok, true);
assert.deepEqual(calls, ["openai/a", "claude/b"]);
});
test("unknown strategy value normalizes to priority order", async () => {
const calls: string[] = [];
const result = await handleComboChat({