mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 16:45:22 +00:00
feat: enable permission-aware subagents
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Model resolution: exact match ("provider/modelId") with fuzzy fallback.
|
||||
*/
|
||||
|
||||
export interface ModelEntry {
|
||||
id: string;
|
||||
name: string;
|
||||
provider: string;
|
||||
}
|
||||
|
||||
export interface ModelRegistry {
|
||||
find(provider: string, modelId: string): any;
|
||||
getAll(): any[];
|
||||
getAvailable?(): any[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a model string to a Model instance.
|
||||
* Tries exact match first ("provider/modelId"), then fuzzy match against all available models.
|
||||
* Returns the Model on success, or an error message string on failure.
|
||||
*/
|
||||
export function resolveModel(
|
||||
input: string,
|
||||
registry: ModelRegistry,
|
||||
): any | string {
|
||||
// Available models (those with auth configured)
|
||||
const all = (registry.getAvailable?.() ?? registry.getAll()) as ModelEntry[];
|
||||
const availableSet = new Set(all.map(m => `${m.provider}/${m.id}`.toLowerCase()));
|
||||
|
||||
// 1. Exact match: "provider/modelId" — only if available (has auth)
|
||||
const slashIdx = input.indexOf("/");
|
||||
if (slashIdx !== -1) {
|
||||
const provider = input.slice(0, slashIdx);
|
||||
const modelId = input.slice(slashIdx + 1);
|
||||
if (availableSet.has(input.toLowerCase())) {
|
||||
const found = registry.find(provider, modelId);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fuzzy match against available models. Normalize separators so cosmetic
|
||||
// punctuation differences still match — e.g. "claude-haiku-4.5" and
|
||||
// "claude-haiku-4-5" (dot vs dash in the version) resolve to the same model.
|
||||
const normalize = (s: string) => s.toLowerCase().replace(/\./g, "-");
|
||||
const query = normalize(input);
|
||||
|
||||
// Score each model: prefer exact id match > id contains > name contains > provider+id contains
|
||||
let bestMatch: ModelEntry | undefined;
|
||||
let bestScore = 0;
|
||||
|
||||
for (const m of all) {
|
||||
const id = normalize(m.id);
|
||||
const name = normalize(m.name);
|
||||
const full = normalize(`${m.provider}/${m.id}`);
|
||||
|
||||
let score = 0;
|
||||
if (id === query || full === query) {
|
||||
score = 100; // exact
|
||||
} else if (id.includes(query) || full.includes(query)) {
|
||||
score = 60 + (query.length / id.length) * 30; // substring, prefer tighter matches
|
||||
} else if (name.includes(query)) {
|
||||
score = 40 + (query.length / name.length) * 20;
|
||||
} else if (
|
||||
// A trailing date-stamp token (e.g. "20251001") is optional, so a
|
||||
// date-pinned config like "claude-haiku-4-5-20251001" still matches an
|
||||
// undated registry id like "claude-haiku-4-5".
|
||||
query
|
||||
.split(/[\s\-/]+/)
|
||||
.every(part => /^\d{8}$/.test(part) || id.includes(part) || name.includes(part) || m.provider.toLowerCase().includes(part))
|
||||
) {
|
||||
score = 20; // all parts present somewhere
|
||||
}
|
||||
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
bestMatch = m;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestMatch && bestScore >= 20) {
|
||||
const found = registry.find(bestMatch.provider, bestMatch.id);
|
||||
if (found) return found;
|
||||
}
|
||||
|
||||
// 3. Provider fallback: a "provider/modelId" query that didn't match under the
|
||||
// named provider (exact or fuzzy above) retries against all providers. The
|
||||
// named provider is preferred when present; this only kicks in when it isn't,
|
||||
// so the same model from another provider beats falling back to "inherit".
|
||||
if (slashIdx !== -1) {
|
||||
const bare = resolveModel(input.slice(slashIdx + 1), registry);
|
||||
if (typeof bare !== "string") return bare;
|
||||
}
|
||||
|
||||
// 4. No match — list available models
|
||||
const modelList = all
|
||||
.map(m => ` ${m.provider}/${m.id}`)
|
||||
.sort()
|
||||
.join("\n");
|
||||
return `Model not found: "${input}".\n\nAvailable models:\n${modelList}`;
|
||||
}
|
||||
Reference in New Issue
Block a user