Files
OmniRoute/open-sse/services/combo/fusionPanel.ts
Praveen K Palaniswamy 65e81158ab fix(ollama): route models by advertised capability (#11088)
Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host.

Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean.

Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes #11087.
2026-08-23 11:45:01 -03:00

84 lines
3.0 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 { getComboModelString, 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;
}
// Provider-wildcard steps have no concrete model to dispatch — fusion is a
// fixed-size panel of literal models/combo-refs, not a wildcard-expanding
// strategy (see file header). Skip rather than push an undefined model.
const modelStr = getComboModelString(step);
if (modelStr) panel.push(modelStr);
});
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,
});
};
}