evOo-local/packages/core/src/model-providers.ts

78 lines
2.7 KiB
TypeScript

import type { ModelCatalogEntry, ModelTier } from "@rakazo/contracts";
export const POPULAR_MODEL_PROVIDER_IDS = [
"openrouter",
"openai-codex",
"anthropic",
"openai",
"google",
"vercel-ai-gateway",
] as const;
const DEFAULT_PROVIDER_COUNT = POPULAR_MODEL_PROVIDER_IDS.length;
const POPULAR_MODEL_PROVIDER_ID_SET = new Set<string>(POPULAR_MODEL_PROVIDER_IDS);
/** Keep provider selection short while ensuring a deployment's current default is never hidden. */
export function featuredModelProviders(
providers: readonly ModelCatalogEntry[],
selectedProvider: string,
): ModelCatalogEntry[] {
const byId = new Map(providers.map((entry) => [entry.provider, entry]));
const ordered = [
...POPULAR_MODEL_PROVIDER_IDS.map((id) => byId.get(id)).filter(
(entry): entry is ModelCatalogEntry => entry !== undefined,
),
...providers.filter((entry) => !POPULAR_MODEL_PROVIDER_ID_SET.has(entry.provider)),
];
const featured = ordered.slice(0, DEFAULT_PROVIDER_COUNT);
const selected = byId.get(selectedProvider);
if (!selected || featured.some((entry) => entry.provider === selectedProvider)) return featured;
return [...featured.slice(0, DEFAULT_PROVIDER_COUNT - 1), selected];
}
/** Return the active choice separately when it is not one of the search results. */
export function selectedProviderOutsideSearchResults(
filteredProviders: readonly ModelCatalogEntry[],
allProviders: readonly ModelCatalogEntry[],
selectedProvider: string,
): ModelCatalogEntry | undefined {
if (filteredProviders.some((entry) => entry.provider === selectedProvider)) {
return undefined;
}
return allProviders.find((entry) => entry.provider === selectedProvider);
}
export type StepRoutingPhase =
| "engagement_judge"
| "memory_consolidation"
| "tool_execution"
| "synthesis";
export type StepRoutingInput = {
phase: StepRoutingPhase;
toolNames?: readonly string[];
promptText?: string;
};
const CODE_TOOL_PATTERNS = /^(bash|shell|exec|terminal|edit_file|write_file|git|computer_)/i;
const CODE_PROMPT_PATTERNS =
/\b(refactor|typescript|vitest|dockerfile|sql|stacktrace|compile|regex|bugfix)\b/i;
/**
* Selects the space model tier best suited for a run step so low-latency background checks use
* the fast tier, coding/sandbox operations use the code tier, and synthesis uses standard.
*/
export function selectStepModelTier(input: StepRoutingInput): ModelTier {
if (input.phase === "engagement_judge" || input.phase === "memory_consolidation") {
return "fast";
}
if (input.toolNames?.some((name) => CODE_TOOL_PATTERNS.test(name.trim()))) {
return "code";
}
if (input.promptText && CODE_PROMPT_PATTERNS.test(input.promptText)) {
return "code";
}
return "standard";
}