pi-mono/apps/p2/plugins/models/scope.ts
Mario Zechner 05dc8d239d feat: add apps/p2 (imported from pi-private at 7dadff697)
p2 (Pi Mobile on Chord v2) moves from pi-private into pi as one commit. Root scripts that loop all workspaces
use --if-present; npm test, ./test.sh and the release script cover packages/* only (p2: cd apps/p2 && npm test);
sync-versions also updates apps' workspace pins.
2026-11-03 13:39:06 +00:00

63 lines
2.5 KiB
TypeScript

// Model scope for cycling (pi's `enabledModels`, coding-agent core/model-resolver.ts `resolveModelScopeFromModels`),
// pure: patterns are `provider/modelId` or `modelId`, a case-insensitive `*`/`?` glob over either, each with an optional
// `:thinkingLevel` suffix. Exact references win over globs; duplicates keep their first position.
import type { ModelInfo } from "./contract.ts";
const LEVELS = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
export type ScopedModel = { provider: string; modelId: string; thinkingLevel?: string };
export type ScopeResult = { models: ScopedModel[]; unmatched: string[] };
function glob(pattern: string): RegExp {
const source = pattern
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
.replace(/\*/g, ".*")
.replace(/\?/g, ".");
return new RegExp(`^${source}$`, "i");
}
function exact(reference: string, models: readonly ModelInfo[]): ModelInfo | undefined {
const lower = reference.toLowerCase();
return (
models.find((model) => `${model.provider}/${model.modelId}`.toLowerCase() === lower) ??
models.find((model) => model.modelId.toLowerCase() === lower)
);
}
export function resolveModelScope(patterns: readonly string[], models: readonly ModelInfo[]): ScopeResult {
const result: ScopeResult = { models: [], unmatched: [] };
const add = (model: ModelInfo, thinkingLevel: string | undefined) => {
if (result.models.some((item) => item.provider === model.provider && item.modelId === model.modelId)) return;
result.models.push({
provider: model.provider,
modelId: model.modelId,
...(thinkingLevel ? { thinkingLevel } : {}),
});
};
for (const pattern of patterns) {
let reference = pattern.trim();
let level: string | undefined;
// A model ID may contain colons (OpenRouter's `:exacto`): the whole pattern first, then a level suffix.
const colon = reference.lastIndexOf(":");
if (!exact(reference, models) && colon !== -1 && LEVELS.has(reference.slice(colon + 1))) {
level = reference.slice(colon + 1);
reference = reference.slice(0, colon);
}
const single = exact(reference, models);
if (single) {
add(single, level);
continue;
}
if (!/[*?]/.test(reference)) {
result.unmatched.push(pattern);
continue;
}
const matcher = glob(reference);
const matching = models.filter(
(model) => matcher.test(`${model.provider}/${model.modelId}`) || matcher.test(model.modelId),
);
if (matching.length === 0) result.unmatched.push(pattern);
for (const model of matching) add(model, level);
}
return result;
}