Files
OmniRoute/open-sse/services/combo/fusionPanel.ts
Diego Rodrigues de Sa e Souza 8b9c7734b8 fix(routing): resolve nested combo-ref panel members in fusion strategy (#6764) (#7259)
Fusion's panel-model extraction in combo.ts only recognized plain string
or {model: string} entries in combo.models; a {kind:"combo-ref", comboName}
step (a first-class, Zod-validated combo-step shape the dashboard already
lets you add to a fusion panel) had neither field, so it was silently
filtered out — no error, no warning, and an opaque 400 if it was the only
panel member.

A combo-ref panel member is now dispatched as one black-box panel voice
(a recursive handleComboChat call into the referenced combo, reusing the
same executeComboRefUnit + cycle/depth guards every other combo-ref-
consuming strategy already uses), not a fan-out of the referenced combo's
own targets.

New module open-sse/services/combo/fusionPanel.ts keeps the frozen
combo.ts god-file's growth minimal (extraction/dispatch-wrapper logic
lives there; the fusion branch itself only wires it in).
2026-07-17 10:41:13 -03:00

80 lines
2.7 KiB
TypeScript

/**
* Fusion panel member extraction — resolves combo.models entries for the
* fusion strategy, including nested `combo-ref` steps (#6764).
*
* A combo-ref panel member is dispatched as ONE black-box panel voice (a full
* recursive handleComboChat call for the referenced combo, reusing the same
* executeComboRefUnit + cycle/depth guards every other combo-ref-consuming
* strategy already uses) — NOT a fan-out of the referenced combo's own
* targets. This keeps panel sizing and cost predictable and matches how a
* literal `auto/*` string panel member already behaves via the single-
* dispatch safety net in src/sse/handlers/chat.ts.
*/
import { normalizeComboStep } from "../../../src/lib/combos/steps.ts";
import { executeComboRefUnit } from "./runtimeUnits.ts";
import type {
ComboCollectionLike,
ComboNestingContext,
HandleComboChatOptions,
HandleSingleModel,
ResolvedComboRefTarget,
} from "./types.ts";
export type FusionPanelSpec = {
/** Dispatch keys handed to fusion.ts's `models` — comboName for combo-ref members, plain model string otherwise. */
panel: string[];
/** comboName -> resolved combo-ref unit, consumed by buildFusionHandleSingleModel. */
comboRefUnits: Map<string, ResolvedComboRefTarget>;
};
export function extractFusionPanelSpec(
models: unknown[],
comboName: string,
allCombos: ComboCollectionLike
): FusionPanelSpec {
const panel: string[] = [];
const comboRefUnits = new Map<string, ResolvedComboRefTarget>();
models.forEach((entry, index) => {
const step = normalizeComboStep(entry, { comboName, index, allCombos });
if (!step) return;
if (step.kind === "combo-ref") {
if (!comboRefUnits.has(step.comboName)) {
comboRefUnits.set(step.comboName, {
kind: "combo-ref",
stepId: step.id,
executionKey: step.id,
comboName: step.comboName,
weight: step.weight,
label: step.label ?? null,
});
}
panel.push(step.comboName);
return;
}
panel.push(step.model);
});
return { panel, comboRefUnits };
}
export function buildFusionHandleSingleModel(args: {
handleSingleModel: HandleSingleModel;
comboRefUnits: Map<string, ResolvedComboRefTarget>;
allCombos: ComboCollectionLike;
nesting: ComboNestingContext;
baseOptions: HandleComboChatOptions;
runCombo: (options: HandleComboChatOptions) => Promise<Response>;
}): HandleSingleModel {
return (body, modelStr, target) => {
const unit = args.comboRefUnits.get(modelStr);
if (!unit) return args.handleSingleModel(body, modelStr, target);
return executeComboRefUnit({
body,
unit,
allCombos: args.allCombos,
runCombo: args.runCombo,
baseOptions: args.baseOptions,
nesting: args.nesting,
});
};
}