@oh-my-pi/pi-coding-agent 15.10.10 → 15.10.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +95 -4
- package/dist/cli.js +23087 -0
- package/dist/tokenizers.linux-x64-gnu-xcjh3jwk.node +0 -0
- package/dist/types/async/job-manager.d.ts +18 -0
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/dry-balance-cli.d.ts +1 -1
- package/dist/types/cli/gallery-cli.d.ts +1 -1
- package/dist/types/cli/gallery-fixtures/types.d.ts +1 -1
- package/dist/types/cli/usage-cli.d.ts +72 -0
- package/dist/types/commands/launch.d.ts +1 -1
- package/dist/types/commands/read.d.ts +1 -1
- package/dist/types/commands/usage.d.ts +25 -0
- package/dist/types/config/append-only-context-mode.d.ts +2 -1
- package/dist/types/config/model-discovery.d.ts +55 -0
- package/dist/types/config/model-registry.d.ts +7 -219
- package/dist/types/config/model-resolver.d.ts +16 -10
- package/dist/types/config/model-roles.d.ts +28 -0
- package/dist/types/config/models-config-schema.d.ts +523 -42
- package/dist/types/config/models-config.d.ts +385 -0
- package/dist/types/config/settings-schema.d.ts +12 -7
- package/dist/types/config/settings.d.ts +1 -1
- package/dist/types/debug/log-viewer.d.ts +1 -1
- package/dist/types/debug/raw-sse.d.ts +1 -1
- package/dist/types/eval/backend.d.ts +0 -2
- package/dist/types/eval/idle-timeout.d.ts +0 -4
- package/dist/types/eval/js/shared/rewrite-imports.d.ts +6 -6
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +3 -3
- package/dist/types/hindsight/mental-models.d.ts +17 -8
- package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/lsp/edits.d.ts +9 -0
- package/dist/types/lsp/index.d.ts +2 -2
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/lsp/utils.d.ts +3 -0
- package/dist/types/mcp/json-rpc.d.ts +5 -0
- package/dist/types/mnemopi/state.d.ts +11 -1
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/bash-execution.d.ts +1 -1
- package/dist/types/modes/components/copy-selector.d.ts +1 -1
- package/dist/types/modes/components/dynamic-border.d.ts +1 -1
- package/dist/types/modes/components/extensions/extension-dashboard.d.ts +1 -1
- package/dist/types/modes/components/extensions/extension-list.d.ts +1 -1
- package/dist/types/modes/components/extensions/inspector-panel.d.ts +1 -1
- package/dist/types/modes/components/footer.d.ts +1 -1
- package/dist/types/modes/components/hook-editor.d.ts +5 -0
- package/dist/types/modes/components/hook-input.d.ts +4 -0
- package/dist/types/modes/components/hook-selector.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +1 -1
- package/dist/types/modes/components/plan-review-overlay.d.ts +1 -1
- package/dist/types/modes/components/session-observer-overlay.d.ts +1 -1
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/status-line/component.d.ts +1 -1
- package/dist/types/modes/components/tiny-title-download-progress.d.ts +1 -1
- package/dist/types/modes/components/transcript-container.d.ts +25 -6
- package/dist/types/modes/components/tree-selector.d.ts +1 -1
- package/dist/types/modes/components/user-message-selector.d.ts +1 -1
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/components/visual-truncate.d.ts +1 -1
- package/dist/types/modes/components/welcome.d.ts +19 -3
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
- package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +1 -1
- package/dist/types/modes/types.d.ts +2 -1
- package/dist/types/session/agent-session.d.ts +1 -1
- package/dist/types/session/auth-broker-config.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +1 -1
- package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +13 -0
- package/dist/types/ssh/connection-manager.d.ts +8 -0
- package/dist/types/task/parallel.d.ts +2 -2
- package/dist/types/task/worktree.d.ts +2 -0
- package/dist/types/tools/ask.d.ts +4 -0
- package/dist/types/tools/conflict-detect.d.ts +16 -0
- package/dist/types/tools/github-cache.d.ts +7 -0
- package/dist/types/tools/sqlite-reader.d.ts +3 -0
- package/dist/types/tui/output-block.d.ts +3 -3
- package/dist/types/utils/changelog.d.ts +8 -0
- package/dist/types/web/scrapers/readthedocs.d.ts +3 -0
- package/dist/types/web/scrapers/types.d.ts +12 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -1
- package/dist/types/web/search/providers/gemini.d.ts +1 -1
- package/examples/extensions/tools.ts +5 -4
- package/package.json +14 -11
- package/scripts/build-binary.ts +18 -23
- package/scripts/bundle-dist.ts +81 -0
- package/scripts/{dev-launch → omp} +1 -1
- package/scripts/{dev-launch-preload.ts → omp.ts} +1 -1
- package/src/async/job-manager.ts +57 -3
- package/src/autoresearch/dashboard.ts +1 -1
- package/src/autoresearch/prompt-setup.md +6 -6
- package/src/autoresearch/prompt.md +6 -6
- package/src/capability/fs.ts +10 -0
- package/src/cli/args.ts +1 -1
- package/src/cli/auth-gateway-cli.ts +1 -3
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli/gallery-fixtures/fs.ts +1 -1
- package/src/cli/gallery-fixtures/types.ts +5 -1
- package/src/cli/list-models.ts +2 -1
- package/src/cli/usage-cli.ts +603 -0
- package/src/cli-commands.ts +1 -0
- package/src/cli.ts +69 -5
- package/src/commands/complete.ts +1 -1
- package/src/commands/launch.ts +1 -1
- package/src/commands/read.ts +6 -3
- package/src/commands/usage.ts +35 -0
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/model-selection.ts +1 -1
- package/src/config/append-only-context-mode.ts +6 -12
- package/src/config/model-discovery.ts +554 -0
- package/src/config/model-registry.ts +231 -1019
- package/src/config/model-resolver.ts +113 -156
- package/src/config/model-roles.ts +74 -0
- package/src/config/models-config-schema.ts +57 -8
- package/src/config/models-config.ts +129 -0
- package/src/config/settings-schema.ts +18 -4
- package/src/config/settings.ts +37 -1
- package/src/dap/client.ts +124 -37
- package/src/dap/session.ts +259 -158
- package/src/debug/log-viewer.ts +1 -1
- package/src/debug/raw-sse.ts +1 -1
- package/src/edit/diff.ts +47 -3
- package/src/edit/hashline/block-resolver.ts +20 -1
- package/src/edit/hashline/diff.ts +36 -1
- package/src/edit/hashline/execute.ts +8 -2
- package/src/edit/index.ts +16 -1
- package/src/edit/modes/patch.ts +52 -0
- package/src/edit/modes/replace.ts +56 -22
- package/src/edit/notebook.ts +22 -2
- package/src/edit/renderer.ts +36 -10
- package/src/eval/__tests__/completion-bridge.test.ts +1 -1
- package/src/eval/backend.ts +0 -2
- package/src/eval/completion-bridge.ts +2 -1
- package/src/eval/idle-timeout.ts +2 -9
- package/src/eval/js/context-manager.ts +6 -8
- package/src/eval/js/executor.ts +6 -2
- package/src/eval/js/index.ts +0 -2
- package/src/eval/js/shared/helpers.ts +5 -6
- package/src/eval/js/shared/local-module-loader.ts +1 -1
- package/src/eval/js/shared/prelude.txt +62 -1
- package/src/eval/js/shared/rewrite-imports.ts +40 -22
- package/src/eval/js/shared/runtime.ts +1 -1
- package/src/eval/py/index.ts +0 -2
- package/src/eval/py/kernel.ts +19 -0
- package/src/eval/py/runner.py +107 -3
- package/src/exec/bash-executor.ts +3 -1
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +3 -1
- package/src/extensibility/extensions/types.ts +3 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +20 -3
- package/src/hindsight/mental-models.ts +59 -12
- package/src/hindsight/state.ts +6 -1
- package/src/internal-urls/artifact-protocol.ts +11 -2
- package/src/internal-urls/docs-index.generated.ts +8 -8
- package/src/internal-urls/issue-pr-protocol.ts +12 -5
- package/src/internal-urls/router.ts +1 -1
- package/src/internal-urls/types.ts +1 -1
- package/src/lib/xai-http.ts +1 -1
- package/src/lsp/client.ts +118 -38
- package/src/lsp/clients/biome-client.ts +101 -39
- package/src/lsp/edits.ts +143 -95
- package/src/lsp/index.ts +31 -22
- package/src/lsp/render.ts +1 -1
- package/src/lsp/types.ts +2 -0
- package/src/lsp/utils.ts +28 -10
- package/src/main.ts +165 -17
- package/src/mcp/json-rpc.ts +35 -5
- package/src/mcp/transports/stdio.ts +7 -1
- package/src/memories/index.ts +2 -1
- package/src/mnemopi/backend.ts +25 -3
- package/src/mnemopi/state.ts +38 -2
- package/src/modes/components/agent-dashboard.ts +10 -7
- package/src/modes/components/assistant-message.ts +19 -13
- package/src/modes/components/bash-execution.ts +1 -1
- package/src/modes/components/copy-selector.ts +1 -1
- package/src/modes/components/diff.ts +13 -2
- package/src/modes/components/dynamic-border.ts +12 -3
- package/src/modes/components/extensions/extension-dashboard.ts +8 -5
- package/src/modes/components/extensions/extension-list.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +1 -1
- package/src/modes/components/footer.ts +1 -1
- package/src/modes/components/history-search.ts +1 -1
- package/src/modes/components/hook-editor.ts +8 -0
- package/src/modes/components/hook-input.ts +8 -0
- package/src/modes/components/hook-selector.ts +2 -2
- package/src/modes/components/model-selector.ts +4 -2
- package/src/modes/components/plan-review-overlay.ts +1 -1
- package/src/modes/components/session-observer-overlay.ts +2 -2
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/settings-selector.ts +5 -1
- package/src/modes/components/status-line/component.ts +1 -1
- package/src/modes/components/tiny-title-download-progress.ts +1 -1
- package/src/modes/components/transcript-container.ts +258 -53
- package/src/modes/components/tree-selector.ts +3 -3
- package/src/modes/components/user-message-selector.ts +1 -1
- package/src/modes/components/user-message.ts +17 -5
- package/src/modes/components/visual-truncate.ts +1 -1
- package/src/modes/components/welcome.ts +108 -26
- package/src/modes/controllers/command-controller.ts +10 -3
- package/src/modes/controllers/event-controller.ts +73 -4
- package/src/modes/controllers/input-controller.ts +1 -1
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/controllers/streaming-reveal.ts +85 -18
- package/src/modes/interactive-mode.ts +3 -9
- package/src/modes/setup-wizard/scenes/glyph.ts +1 -1
- package/src/modes/setup-wizard/scenes/providers.ts +1 -1
- package/src/modes/setup-wizard/scenes/sign-in.ts +1 -1
- package/src/modes/setup-wizard/scenes/theme.ts +1 -1
- package/src/modes/setup-wizard/scenes/types.ts +1 -1
- package/src/modes/setup-wizard/scenes/web-search.ts +1 -1
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/types.ts +2 -1
- package/src/prompts/agents/explore.md +2 -2
- package/src/prompts/agents/librarian.md +1 -2
- package/src/prompts/agents/oracle.md +1 -1
- package/src/prompts/agents/plan.md +5 -5
- package/src/prompts/agents/task.md +5 -5
- package/src/prompts/ci-green-request.md +5 -7
- package/src/prompts/goals/goal-budget-limit.md +2 -2
- package/src/prompts/goals/goal-continuation.md +4 -4
- package/src/prompts/goals/goal-mode-active.md +1 -1
- package/src/prompts/memories/read-path.md +1 -1
- package/src/prompts/memories/stage_one_system.md +2 -2
- package/src/prompts/review-custom-request.md +1 -1
- package/src/prompts/system/agent-creation-architect.md +2 -2
- package/src/prompts/system/auto-continue.md +1 -1
- package/src/prompts/system/background-tan-dispatch.md +1 -1
- package/src/prompts/system/btw-user.md +2 -2
- package/src/prompts/system/commit-message-system.md +13 -1
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/eager-todo.md +2 -2
- package/src/prompts/system/irc-incoming.md +1 -1
- package/src/prompts/system/manual-continue.md +1 -1
- package/src/prompts/system/omfg-user.md +3 -4
- package/src/prompts/system/orchestrate-notice.md +9 -9
- package/src/prompts/system/plan-mode-active.md +4 -4
- package/src/prompts/system/plan-mode-subagent.md +4 -5
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +1 -1
- package/src/prompts/system/project-prompt.md +2 -2
- package/src/prompts/system/subagent-system-prompt.md +4 -4
- package/src/prompts/system/system-prompt.md +13 -24
- package/src/prompts/system/title-system.md +2 -2
- package/src/prompts/system/ttsr-tool-reminder.md +1 -1
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/ast-edit.md +1 -1
- package/src/prompts/tools/ast-grep.md +2 -2
- package/src/prompts/tools/bash.md +5 -7
- package/src/prompts/tools/browser.md +7 -7
- package/src/prompts/tools/debug.md +1 -1
- package/src/prompts/tools/eval.md +3 -3
- package/src/prompts/tools/find.md +0 -1
- package/src/prompts/tools/github.md +8 -7
- package/src/prompts/tools/goal.md +1 -1
- package/src/prompts/tools/image-gen.md +1 -1
- package/src/prompts/tools/inspect-image-system.md +1 -1
- package/src/prompts/tools/irc.md +15 -15
- package/src/prompts/tools/lsp.md +2 -2
- package/src/prompts/tools/patch.md +2 -2
- package/src/prompts/tools/read.md +3 -4
- package/src/prompts/tools/recall.md +1 -1
- package/src/prompts/tools/reflect.md +1 -1
- package/src/prompts/tools/render-mermaid.md +2 -2
- package/src/prompts/tools/replace.md +4 -10
- package/src/prompts/tools/rewind.md +2 -2
- package/src/prompts/tools/search-tool-bm25.md +1 -9
- package/src/prompts/tools/search.md +0 -1
- package/src/prompts/tools/ssh.md +0 -4
- package/src/prompts/tools/task.md +2 -3
- package/src/prompts/tools/todo.md +1 -1
- package/src/sdk.ts +23 -10
- package/src/session/agent-session.ts +44 -10
- package/src/session/auth-broker-config.ts +30 -1
- package/src/session/session-manager.ts +2 -2
- package/src/session/streaming-output.ts +23 -2
- package/src/slash-commands/builtin-registry.ts +20 -0
- package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
- package/src/ssh/connection-manager.ts +27 -0
- package/src/task/commands.ts +2 -1
- package/src/task/executor.ts +61 -53
- package/src/task/index.ts +137 -60
- package/src/task/parallel.ts +3 -3
- package/src/task/render.ts +2 -2
- package/src/task/worktree.ts +64 -56
- package/src/thinking.ts +2 -1
- package/src/tiny/title-client.ts +26 -11
- package/src/tools/archive-reader.ts +30 -2
- package/src/tools/ask.ts +104 -21
- package/src/tools/ast-edit.ts +25 -5
- package/src/tools/auto-generated-guard.ts +20 -3
- package/src/tools/bash-interactive.ts +27 -7
- package/src/tools/bash.ts +54 -13
- package/src/tools/browser/launch.ts +11 -2
- package/src/tools/browser/readable.ts +19 -2
- package/src/tools/browser/registry.ts +4 -1
- package/src/tools/browser/render.ts +2 -2
- package/src/tools/browser/tab-supervisor.ts +55 -16
- package/src/tools/conflict-detect.ts +50 -4
- package/src/tools/debug.ts +1 -1
- package/src/tools/eval-render.ts +5 -5
- package/src/tools/eval.ts +0 -2
- package/src/tools/fetch.ts +33 -10
- package/src/tools/gh-cache-invalidation.ts +63 -8
- package/src/tools/gh-renderer.ts +1 -1
- package/src/tools/gh.ts +172 -29
- package/src/tools/github-cache.ts +70 -6
- package/src/tools/image-gen.ts +3 -9
- package/src/tools/irc.ts +5 -1
- package/src/tools/job.ts +1 -1
- package/src/tools/read.ts +202 -61
- package/src/tools/render-utils.ts +3 -3
- package/src/tools/resolve.ts +1 -1
- package/src/tools/search.ts +92 -29
- package/src/tools/sqlite-reader.ts +17 -5
- package/src/tools/ssh.ts +8 -8
- package/src/tools/todo.ts +38 -8
- package/src/tools/write.ts +118 -18
- package/src/tui/output-block.ts +4 -4
- package/src/utils/changelog.ts +27 -1
- package/src/utils/file-mentions.ts +2 -1
- package/src/web/scrapers/arxiv.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +1 -1
- package/src/web/scrapers/iacr.ts +1 -1
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/twitter.ts +2 -1
- package/src/web/scrapers/types.ts +87 -8
- package/src/web/scrapers/wikipedia.ts +1 -1
- package/src/web/scrapers/youtube.ts +6 -1
- package/src/web/search/index.ts +1 -1
- package/src/web/search/providers/codex.ts +2 -1
- package/src/web/search/providers/gemini.ts +2 -3
- package/src/web/search/render.ts +8 -6
- package/dist/types/config/model-equivalence.d.ts +0 -24
- package/dist/types/config/model-id-affixes.d.ts +0 -12
- package/dist/types/config/model-provider-priority.d.ts +0 -1
- package/dist/types/exec/idle-timeout-watchdog.d.ts +0 -18
- package/src/config/model-equivalence.ts +0 -875
- package/src/config/model-id-affixes.ts +0 -81
- package/src/config/model-provider-priority.ts +0 -56
- package/src/exec/idle-timeout-watchdog.ts +0 -126
|
@@ -1,875 +0,0 @@
|
|
|
1
|
-
import { type Api, getBundledModels, getBundledProviders, type Model } from "@oh-my-pi/pi-ai";
|
|
2
|
-
import {
|
|
3
|
-
getBracketStrippedModelIdCandidates,
|
|
4
|
-
getLongestModelLikeIdSegment,
|
|
5
|
-
getModelLikeIdSegments,
|
|
6
|
-
} from "./model-id-affixes";
|
|
7
|
-
|
|
8
|
-
export type CanonicalModelSource = "override" | "bundled" | "heuristic" | "fallback";
|
|
9
|
-
|
|
10
|
-
export interface ModelEquivalenceConfig {
|
|
11
|
-
overrides?: Record<string, string>;
|
|
12
|
-
exclude?: string[];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface CanonicalModelVariant {
|
|
16
|
-
canonicalId: string;
|
|
17
|
-
selector: string;
|
|
18
|
-
model: Model<Api>;
|
|
19
|
-
source: CanonicalModelSource;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface CanonicalModelRecord {
|
|
23
|
-
id: string;
|
|
24
|
-
name: string;
|
|
25
|
-
variants: CanonicalModelVariant[];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface CanonicalModelIndex {
|
|
29
|
-
records: CanonicalModelRecord[];
|
|
30
|
-
byId: Map<string, CanonicalModelRecord>;
|
|
31
|
-
bySelector: Map<string, string>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
interface CanonicalReferenceData {
|
|
35
|
-
references: Map<string, Model<Api>>;
|
|
36
|
-
officialIds: Set<string>;
|
|
37
|
-
suffixAliases: Map<string, string>;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
interface CompiledEquivalenceConfig {
|
|
41
|
-
overrides: Map<string, string>;
|
|
42
|
-
exclude: Set<string>;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface ResolvedCanonicalModel {
|
|
46
|
-
id: string;
|
|
47
|
-
source: CanonicalModelSource;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const TRAILING_MARKER_PATTERN =
|
|
51
|
-
/[-:](?:thinking|customtools|high|low|medium|minimal|xhigh|free|cloud|exacto|nitro|original|optimized|nvfp4|fp8|fp4|bf16|int8|int4)$/i;
|
|
52
|
-
const WRAPPER_PREFIXES = ["duo-chat-"] as const;
|
|
53
|
-
|
|
54
|
-
let referenceDataCache: CanonicalReferenceData | undefined;
|
|
55
|
-
const EMPTY_COMPILED_EQUIVALENCE: CompiledEquivalenceConfig = {
|
|
56
|
-
overrides: new Map<string, string>(),
|
|
57
|
-
exclude: new Set<string>(),
|
|
58
|
-
};
|
|
59
|
-
const kModelResolutionCache = Symbol("model-equivalence.resolutionCache");
|
|
60
|
-
interface CompiledEquivalenceConfigWithCache extends CompiledEquivalenceConfig {
|
|
61
|
-
[kModelResolutionCache]?: Map<string, ResolvedCanonicalModel>;
|
|
62
|
-
}
|
|
63
|
-
const FAMILY_EXTRACTION_PATTERNS = [
|
|
64
|
-
/(?:^|[/:._-])((?:claude|gemini|gpt|grok|glm|qwen|minimax|kimi|deepseek|llama|gemma|nova|mistral|ministral|pixtral|codestral|devstral|magistral|ernie|doubao|seed|aion|olmo|molmo|nemotron|palmyra|command|codex|coder|o[1345])[-a-z0-9.]+)(?::|$)/i,
|
|
65
|
-
/(?:^|[/:._-])((?:claude|gemini|gpt|grok|glm|qwen|minimax|kimi|deepseek|llama|gemma|nova|mistral|ministral|pixtral|codestral|devstral|magistral|ernie|doubao|seed|aion|olmo|molmo|nemotron|palmyra|command|codex|coder|o[1345])[-a-z0-9.]+(?:[-_/][a-z0-9.]+)*)(?::|$)/i,
|
|
66
|
-
] as const;
|
|
67
|
-
|
|
68
|
-
function shouldReplaceReference(existing: Model<Api> | undefined, candidate: Model<Api>): boolean {
|
|
69
|
-
if (!existing) return true;
|
|
70
|
-
if (candidate.contextWindow !== existing.contextWindow) {
|
|
71
|
-
return candidate.contextWindow > existing.contextWindow;
|
|
72
|
-
}
|
|
73
|
-
if (candidate.maxTokens !== existing.maxTokens) {
|
|
74
|
-
return candidate.maxTokens > existing.maxTokens;
|
|
75
|
-
}
|
|
76
|
-
return existing.provider !== "openai" && candidate.provider === "openai";
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function buildCanonicalSuffixAliasMap(references: ReadonlyMap<string, Model<Api>>): Map<string, string> {
|
|
80
|
-
const aliases = new Map<string, string>();
|
|
81
|
-
for (const reference of references.values()) {
|
|
82
|
-
const slashIndex = reference.id.lastIndexOf("/");
|
|
83
|
-
if (slashIndex === -1) {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
const suffix = reference.id.slice(slashIndex + 1);
|
|
87
|
-
const alias = getLongestModelLikeIdSegment(suffix);
|
|
88
|
-
if (!alias) {
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
const existing = aliases.get(alias);
|
|
92
|
-
if (!existing || compareCandidatePreference(reference.id, existing) < 0) {
|
|
93
|
-
aliases.set(alias, reference.id);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return new Map([...aliases.entries()].map(([alias, referenceId]) => [normalizeCanonicalIdKey(alias), referenceId]));
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function createCanonicalReferenceData(): CanonicalReferenceData {
|
|
100
|
-
if (referenceDataCache) {
|
|
101
|
-
return referenceDataCache;
|
|
102
|
-
}
|
|
103
|
-
const references = new Map<string, Model<Api>>();
|
|
104
|
-
for (const provider of getBundledProviders()) {
|
|
105
|
-
for (const model of getBundledModels(provider as Parameters<typeof getBundledModels>[0])) {
|
|
106
|
-
const candidate = model as Model<Api>;
|
|
107
|
-
const existing = references.get(candidate.id);
|
|
108
|
-
if (shouldReplaceReference(existing, candidate)) {
|
|
109
|
-
references.set(candidate.id, candidate);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
const officialIds = new Set(references.keys());
|
|
114
|
-
const suffixAliases = buildCanonicalSuffixAliasMap(references);
|
|
115
|
-
referenceDataCache = {
|
|
116
|
-
references: Object.freeze(references) as Map<string, Model<Api>>,
|
|
117
|
-
officialIds: Object.freeze(officialIds) as Set<string>,
|
|
118
|
-
suffixAliases: Object.freeze(suffixAliases) as Map<string, string>,
|
|
119
|
-
};
|
|
120
|
-
return referenceDataCache;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function normalizeSelectorKey(selector: string): string {
|
|
124
|
-
return selector.trim().toLowerCase();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function normalizeCanonicalIdKey(canonicalId: string): string {
|
|
128
|
-
return canonicalId.trim().toLowerCase();
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function getCanonicalSuffixAliasKey(candidate: string): string {
|
|
132
|
-
return PENALTY_HAS_UPPERCASE.test(candidate) ? normalizeCanonicalIdKey(candidate) : candidate;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export function formatCanonicalVariantSelector(model: Model<Api>): string {
|
|
136
|
-
return `${model.provider}/${model.id}`;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function getModelResolutionCacheKey(model: Model<Api>): string {
|
|
140
|
-
return `${model.provider}\0${model.id}`;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function buildOverrideMap(overrides: Record<string, string> | undefined): Map<string, string> {
|
|
144
|
-
const result = new Map<string, string>();
|
|
145
|
-
if (!overrides) {
|
|
146
|
-
return result;
|
|
147
|
-
}
|
|
148
|
-
for (const [selector, canonicalId] of Object.entries(overrides)) {
|
|
149
|
-
const normalizedSelector = normalizeSelectorKey(selector);
|
|
150
|
-
const normalizedCanonicalId = canonicalId.trim();
|
|
151
|
-
if (!normalizedSelector || !normalizedCanonicalId) {
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
result.set(normalizedSelector, normalizedCanonicalId);
|
|
155
|
-
}
|
|
156
|
-
return result;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function buildExclusionSet(exclusions: readonly string[] | undefined): Set<string> {
|
|
160
|
-
const result = new Set<string>();
|
|
161
|
-
for (const selector of exclusions ?? []) {
|
|
162
|
-
const normalized = normalizeSelectorKey(selector);
|
|
163
|
-
if (normalized) {
|
|
164
|
-
result.add(normalized);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return result;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const compiledEquivalenceCache = new WeakMap<ModelEquivalenceConfig, CompiledEquivalenceConfig>();
|
|
171
|
-
function compileEquivalenceConfig(config: ModelEquivalenceConfig | undefined): CompiledEquivalenceConfig {
|
|
172
|
-
if (config) {
|
|
173
|
-
const cached = compiledEquivalenceCache.get(config);
|
|
174
|
-
if (cached) {
|
|
175
|
-
return cached;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
const overrides = buildOverrideMap(config?.overrides);
|
|
179
|
-
const exclude = buildExclusionSet(config?.exclude);
|
|
180
|
-
if (overrides.size === 0 && exclude.size === 0) {
|
|
181
|
-
return EMPTY_COMPILED_EQUIVALENCE;
|
|
182
|
-
}
|
|
183
|
-
const compiled: CompiledEquivalenceConfig = { overrides, exclude };
|
|
184
|
-
if (config) {
|
|
185
|
-
compiledEquivalenceCache.set(config, compiled);
|
|
186
|
-
}
|
|
187
|
-
return compiled;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function addCanonicalCandidate(candidates: Set<string>, candidate: string): void {
|
|
191
|
-
const normalized = candidate.trim();
|
|
192
|
-
if (normalized) {
|
|
193
|
-
candidates.add(normalized);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function stripTrailingMarker(candidate: string): string | undefined {
|
|
198
|
-
const match = TRAILING_MARKER_PATTERN.exec(candidate);
|
|
199
|
-
return match ? candidate.slice(0, match.index) : undefined;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function hasTrailingMarker(candidate: string): boolean {
|
|
203
|
-
return TRAILING_MARKER_PATTERN.test(candidate);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function lowercaseCandidate(candidate: string): string | undefined {
|
|
207
|
-
const lowercased = candidate.toLowerCase();
|
|
208
|
-
return lowercased !== candidate ? lowercased : undefined;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const STRIP_SYNTHETIC_PREFIX_PATTERN = /^hf:/i;
|
|
212
|
-
const STRIP_LATEST_SUFFIX_PATTERN = /-latest$/i;
|
|
213
|
-
const STRIP_LEGACY_GLM_TURBO_PATTERN = /^(glm-4(?:\.\d+)?v?)-turbo$/i;
|
|
214
|
-
const REORDER_ANTHROPIC_FAMILY_PATTERN = /^claude-(\d+(?:[.-]\d+)+)-(opus|sonnet|haiku)$/i;
|
|
215
|
-
const STRIP_PROVIDER_VERSION_SUFFIX_PATTERN = /-v\d+(?::\d+)?$/i;
|
|
216
|
-
const STRIP_DATE_SUFFIX_PATTERN = /-\d{8}$/i;
|
|
217
|
-
const INSERT_ATTACHED_FAMILY_VERSION_SEPARATOR_PATTERN =
|
|
218
|
-
/(^|[/:._-])((?:claude|gemini|gpt|grok|glm|qwen|minimax|kimi|deepseek|llama|gemma|nova|mistral|ministral|pixtral|codestral|devstral|magistral|ernie|doubao|seed|aion|olmo|molmo|nemotron|palmyra|command|codex|coder))(\d+(?:[.-]\d+)*)(?=$|[-_/.:a-z])/gi;
|
|
219
|
-
const SERIES_MINOR_DOT_TO_DASH_PATTERN = /(^|[/:._-])([a-z])(\d)\.(\d)(?=$|[-_/.:a-z])/gi;
|
|
220
|
-
const SERIES_MINOR_DASH_TO_DOT_PATTERN = /(^|[/:._-])([a-z])(\d)-(\d)(?=$|[-_/.:a-z])/gi;
|
|
221
|
-
const EXPAND_COMPACT_SERIES_MINOR_PATTERN = /(^|[/:._-])([a-z])(\d)(\d)(?=$|[-_/.:a-z])/gi;
|
|
222
|
-
const NAMESPACE_SUFFIX_BOUNDARY_PATTERN = /[/:.]/;
|
|
223
|
-
const NAMESPACE_SUFFIX_ALPHA_PATTERN = /[a-z]/i;
|
|
224
|
-
const NAMESPACE_SUFFIX_DIGIT_PATTERN = /\d/;
|
|
225
|
-
const SHORT_VERSION_DOT_TO_DASH_PATTERN = /(^|[-_/])(\d{1,2})\.(\d{1,2})(?=$|[-_a-z])/gi;
|
|
226
|
-
const SHORT_VERSION_DASH_TO_DOT_PATTERN = /(^|[-_/])(\d{1,2})-(\d{1,2})(?=$|[-_a-z])/gi;
|
|
227
|
-
const EXPAND_COMPACT_MINOR_PATTERN = /(^|[-_/])(\d)(\d)(?=$|[-_a-z])/g;
|
|
228
|
-
|
|
229
|
-
function stripSyntheticPrefix(candidate: string): string | undefined {
|
|
230
|
-
const stripped = candidate.replace(STRIP_SYNTHETIC_PREFIX_PATTERN, "");
|
|
231
|
-
return stripped !== candidate ? stripped : undefined;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function stripLatestSuffix(candidate: string): string | undefined {
|
|
235
|
-
const stripped = candidate.replace(STRIP_LATEST_SUFFIX_PATTERN, "");
|
|
236
|
-
return stripped !== candidate ? stripped : undefined;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function stripLegacyGlmTurboSuffix(candidate: string): string | undefined {
|
|
240
|
-
const stripped = candidate.replace(STRIP_LEGACY_GLM_TURBO_PATTERN, "$1");
|
|
241
|
-
return stripped !== candidate ? stripped : undefined;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function reorderAnthropicFamily(candidate: string): string | undefined {
|
|
245
|
-
const match = REORDER_ANTHROPIC_FAMILY_PATTERN.exec(candidate);
|
|
246
|
-
if (!match) {
|
|
247
|
-
return undefined;
|
|
248
|
-
}
|
|
249
|
-
const [, version, family] = match;
|
|
250
|
-
return `claude-${family.toLowerCase()}-${version}`;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
function stripProviderVersionSuffix(candidate: string): string | undefined {
|
|
254
|
-
const stripped = candidate.replace(STRIP_PROVIDER_VERSION_SUFFIX_PATTERN, "");
|
|
255
|
-
return stripped !== candidate ? stripped : undefined;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
function stripDateSuffix(candidate: string): string | undefined {
|
|
259
|
-
const stripped = candidate.replace(STRIP_DATE_SUFFIX_PATTERN, "");
|
|
260
|
-
return stripped !== candidate ? stripped : undefined;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function insertAttachedFamilyVersionSeparator(candidate: string): string | undefined {
|
|
264
|
-
const inserted = candidate.replace(INSERT_ATTACHED_FAMILY_VERSION_SEPARATOR_PATTERN, "$1$2-$3");
|
|
265
|
-
return inserted !== candidate ? inserted : undefined;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function toggleSeriesMinorVersionSeparators(candidate: string): string[] {
|
|
269
|
-
const toggled = new Set<string>();
|
|
270
|
-
const dotToDash = candidate.replace(SERIES_MINOR_DOT_TO_DASH_PATTERN, "$1$2$3-$4");
|
|
271
|
-
if (dotToDash !== candidate) {
|
|
272
|
-
toggled.add(dotToDash);
|
|
273
|
-
}
|
|
274
|
-
const dashToDot = candidate.replace(SERIES_MINOR_DASH_TO_DOT_PATTERN, "$1$2$3.$4");
|
|
275
|
-
if (dashToDot !== candidate) {
|
|
276
|
-
toggled.add(dashToDot);
|
|
277
|
-
}
|
|
278
|
-
return [...toggled];
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function expandCompactSeriesMinorVersions(candidate: string): string[] {
|
|
282
|
-
const expanded = new Set<string>();
|
|
283
|
-
const compactToDash = candidate.replace(EXPAND_COMPACT_SERIES_MINOR_PATTERN, "$1$2$3-$4");
|
|
284
|
-
if (compactToDash !== candidate) {
|
|
285
|
-
expanded.add(compactToDash);
|
|
286
|
-
}
|
|
287
|
-
const compactToDot = candidate.replace(EXPAND_COMPACT_SERIES_MINOR_PATTERN, "$1$2$3.$4");
|
|
288
|
-
if (compactToDot !== candidate) {
|
|
289
|
-
expanded.add(compactToDot);
|
|
290
|
-
}
|
|
291
|
-
return [...expanded];
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Bounded FIFO memo: pure function of `candidate`. Cached arrays are read-only at
|
|
295
|
-
// every callsite (they are iterated to push into a queue — never mutated), so we
|
|
296
|
-
// safely return the same instance. Cap keeps memory bounded under adversarial
|
|
297
|
-
// model-id churn.
|
|
298
|
-
const QUALIFIED_NAMESPACE_SUFFIX_CACHE = new Map<string, string[]>();
|
|
299
|
-
const QUALIFIED_NAMESPACE_SUFFIX_CACHE_CAP = 4096;
|
|
300
|
-
function getQualifiedNamespaceSuffixes(candidate: string): string[] {
|
|
301
|
-
const cached = QUALIFIED_NAMESPACE_SUFFIX_CACHE.get(candidate);
|
|
302
|
-
if (cached !== undefined) {
|
|
303
|
-
return cached;
|
|
304
|
-
}
|
|
305
|
-
const results = new Set<string>();
|
|
306
|
-
for (let index = 1; index < candidate.length; index += 1) {
|
|
307
|
-
if (!NAMESPACE_SUFFIX_BOUNDARY_PATTERN.test(candidate[index - 1]!)) {
|
|
308
|
-
continue;
|
|
309
|
-
}
|
|
310
|
-
const suffix = candidate.slice(index);
|
|
311
|
-
if (suffix.length < 4) {
|
|
312
|
-
continue;
|
|
313
|
-
}
|
|
314
|
-
if (!NAMESPACE_SUFFIX_ALPHA_PATTERN.test(suffix) || !NAMESPACE_SUFFIX_DIGIT_PATTERN.test(suffix)) {
|
|
315
|
-
continue;
|
|
316
|
-
}
|
|
317
|
-
addCanonicalCandidate(results, suffix);
|
|
318
|
-
}
|
|
319
|
-
const output = [...results];
|
|
320
|
-
if (QUALIFIED_NAMESPACE_SUFFIX_CACHE.size >= QUALIFIED_NAMESPACE_SUFFIX_CACHE_CAP) {
|
|
321
|
-
const oldest = QUALIFIED_NAMESPACE_SUFFIX_CACHE.keys().next().value;
|
|
322
|
-
if (oldest !== undefined) {
|
|
323
|
-
QUALIFIED_NAMESPACE_SUFFIX_CACHE.delete(oldest);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
QUALIFIED_NAMESPACE_SUFFIX_CACHE.set(candidate, output);
|
|
327
|
-
return output;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function extractUpstreamFamilyCandidate(candidate: string): string | undefined {
|
|
331
|
-
for (const pattern of FAMILY_EXTRACTION_PATTERNS) {
|
|
332
|
-
const match = pattern.exec(candidate);
|
|
333
|
-
if (match?.[1]) {
|
|
334
|
-
return match[1];
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
return undefined;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const PENALTY_DATE_SUFFIX = /-\d{8}$/i;
|
|
341
|
-
const PENALTY_PROVIDER_VERSION_SUFFIX = /-v\d+(?::\d+)?$/i;
|
|
342
|
-
const PENALTY_HAS_UPPERCASE = /[A-Z]/;
|
|
343
|
-
const PENALTY_CLAUDE_LEADING_VERSION = /^claude-\d/i;
|
|
344
|
-
const PENALTY_CLAUDE_LEGACY_DATE = /^claude-(?:opus|sonnet|haiku)-\d{2}(?=$|[-_a-z])/i;
|
|
345
|
-
const PENALTY_LETTER_DIGIT_DIGIT = /(?:^|[/:._-])[a-z]\d-\d(?=$|[-_/.:a-z])/i;
|
|
346
|
-
const PENALTY_DIGIT_DIGIT = /(?:^|[-_/])\d-\d(?=$|[-_a-z])/;
|
|
347
|
-
const PENALTY_CLAUDE_FAMILY_DIGIT_DIGIT = /^claude-(?:opus|sonnet|haiku)-\d-\d/i;
|
|
348
|
-
|
|
349
|
-
function getCandidatePenalty(candidate: string): number {
|
|
350
|
-
let penalty = 0;
|
|
351
|
-
if (candidate.includes("/")) {
|
|
352
|
-
penalty += 100;
|
|
353
|
-
}
|
|
354
|
-
if (candidate.includes(":")) {
|
|
355
|
-
penalty += 40;
|
|
356
|
-
}
|
|
357
|
-
if (PENALTY_DATE_SUFFIX.test(candidate)) {
|
|
358
|
-
penalty += 25;
|
|
359
|
-
}
|
|
360
|
-
if (PENALTY_PROVIDER_VERSION_SUFFIX.test(candidate)) {
|
|
361
|
-
penalty += 25;
|
|
362
|
-
}
|
|
363
|
-
if (hasTrailingMarker(candidate)) {
|
|
364
|
-
penalty += 20;
|
|
365
|
-
}
|
|
366
|
-
if (PENALTY_HAS_UPPERCASE.test(candidate)) {
|
|
367
|
-
penalty += 10;
|
|
368
|
-
}
|
|
369
|
-
if (PENALTY_CLAUDE_LEADING_VERSION.test(candidate)) {
|
|
370
|
-
penalty += 20;
|
|
371
|
-
}
|
|
372
|
-
if (PENALTY_CLAUDE_LEGACY_DATE.test(candidate)) {
|
|
373
|
-
penalty += 10;
|
|
374
|
-
}
|
|
375
|
-
if (PENALTY_LETTER_DIGIT_DIGIT.test(candidate)) {
|
|
376
|
-
penalty += 6;
|
|
377
|
-
}
|
|
378
|
-
if (PENALTY_DIGIT_DIGIT.test(candidate) && !PENALTY_CLAUDE_FAMILY_DIGIT_DIGIT.test(candidate)) {
|
|
379
|
-
penalty += 4;
|
|
380
|
-
}
|
|
381
|
-
penalty += candidate.length * 0.01;
|
|
382
|
-
return penalty;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
function compareCandidatePreference(left: string, right: string): number {
|
|
386
|
-
const penaltyDiff = getCandidatePenalty(left) - getCandidatePenalty(right);
|
|
387
|
-
if (penaltyDiff !== 0) {
|
|
388
|
-
return penaltyDiff;
|
|
389
|
-
}
|
|
390
|
-
if (left.length !== right.length) {
|
|
391
|
-
return left.length - right.length;
|
|
392
|
-
}
|
|
393
|
-
return left.localeCompare(right);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
function selectBestOfficialCandidate(candidates: readonly string[]): string | undefined {
|
|
397
|
-
if (candidates.length === 0) {
|
|
398
|
-
return undefined;
|
|
399
|
-
}
|
|
400
|
-
let bestCandidate: string | undefined;
|
|
401
|
-
let bestPenalty = 0;
|
|
402
|
-
let bestLength = 0;
|
|
403
|
-
for (const candidate of candidates) {
|
|
404
|
-
const penalty = getCandidatePenalty(candidate);
|
|
405
|
-
const length = candidate.length;
|
|
406
|
-
if (bestCandidate === undefined) {
|
|
407
|
-
bestCandidate = candidate;
|
|
408
|
-
bestPenalty = penalty;
|
|
409
|
-
bestLength = length;
|
|
410
|
-
continue;
|
|
411
|
-
}
|
|
412
|
-
if (penalty < bestPenalty) {
|
|
413
|
-
bestCandidate = candidate;
|
|
414
|
-
bestPenalty = penalty;
|
|
415
|
-
bestLength = length;
|
|
416
|
-
continue;
|
|
417
|
-
}
|
|
418
|
-
if (penalty > bestPenalty) {
|
|
419
|
-
continue;
|
|
420
|
-
}
|
|
421
|
-
if (length < bestLength) {
|
|
422
|
-
bestCandidate = candidate;
|
|
423
|
-
bestLength = length;
|
|
424
|
-
continue;
|
|
425
|
-
}
|
|
426
|
-
if (length > bestLength) {
|
|
427
|
-
continue;
|
|
428
|
-
}
|
|
429
|
-
if (candidate.localeCompare(bestCandidate) < 0) {
|
|
430
|
-
bestCandidate = candidate;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
return bestCandidate;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
function getWrapperCanonicalCandidates(candidate: string): string[] {
|
|
437
|
-
const results = new Set<string>();
|
|
438
|
-
for (const prefix of WRAPPER_PREFIXES) {
|
|
439
|
-
if (!candidate.toLowerCase().startsWith(prefix)) {
|
|
440
|
-
continue;
|
|
441
|
-
}
|
|
442
|
-
const stripped = candidate.slice(prefix.length);
|
|
443
|
-
addCanonicalCandidate(results, stripped);
|
|
444
|
-
if (/^(opus|sonnet|haiku)-/i.test(stripped)) {
|
|
445
|
-
addCanonicalCandidate(results, `claude-${stripped}`);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
return [...results];
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
function getAnthropicAliasOfficial(candidate: string, officialIds: Set<string>): string | undefined {
|
|
452
|
-
const reordered = reorderAnthropicFamily(candidate);
|
|
453
|
-
if (!reordered) {
|
|
454
|
-
return undefined;
|
|
455
|
-
}
|
|
456
|
-
const candidates = [reordered, ...toggleShortVersionSeparators(reordered)].filter(officialId =>
|
|
457
|
-
officialIds.has(officialId),
|
|
458
|
-
);
|
|
459
|
-
return selectBestOfficialCandidate(candidates);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
function compareVersionSegments(left: readonly number[], right: readonly number[]): number {
|
|
463
|
-
const maxLength = Math.max(left.length, right.length);
|
|
464
|
-
for (let index = 0; index < maxLength; index += 1) {
|
|
465
|
-
const diff = (left[index] ?? Number.NEGATIVE_INFINITY) - (right[index] ?? Number.NEGATIVE_INFINITY);
|
|
466
|
-
if (diff !== 0) {
|
|
467
|
-
return diff;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
return 0;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
function parseClaudeFamilyVersionSegments(candidate: string, prefix: string): number[] {
|
|
474
|
-
const normalizedCandidate = candidate.toLowerCase();
|
|
475
|
-
const normalizedPrefix = prefix.toLowerCase();
|
|
476
|
-
if (!normalizedCandidate.startsWith(`${normalizedPrefix}-`)) {
|
|
477
|
-
return [];
|
|
478
|
-
}
|
|
479
|
-
const rawSuffix = normalizedCandidate.slice(normalizedPrefix.length + 1);
|
|
480
|
-
if (!rawSuffix) {
|
|
481
|
-
return [];
|
|
482
|
-
}
|
|
483
|
-
const versionSegments: number[] = [];
|
|
484
|
-
for (const token of rawSuffix.split("-")) {
|
|
485
|
-
if (!token) {
|
|
486
|
-
break;
|
|
487
|
-
}
|
|
488
|
-
if (/^\d{8}$/.test(token)) {
|
|
489
|
-
break;
|
|
490
|
-
}
|
|
491
|
-
if (/^\d{2}$/.test(token)) {
|
|
492
|
-
versionSegments.push(Number(token[0]), Number(token[1]));
|
|
493
|
-
continue;
|
|
494
|
-
}
|
|
495
|
-
if (/^\d+(?:\.\d+)*$/.test(token)) {
|
|
496
|
-
versionSegments.push(...token.split(".").map(part => Number(part)));
|
|
497
|
-
continue;
|
|
498
|
-
}
|
|
499
|
-
break;
|
|
500
|
-
}
|
|
501
|
-
return versionSegments;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
const CLAUDE_FAMILY_ALIAS_PATTERN = /^(?:anthropic\/)?(claude(?:-\d(?:[.-]\d+)?)?-(?:haiku|opus|sonnet))(?:-latest)?$/i;
|
|
505
|
-
const CLAUDE_DATE_SUFFIX_PATTERN = /-\d{8}(?:$|-)/i;
|
|
506
|
-
|
|
507
|
-
function getClaudeFamilyAliasOfficial(candidate: string, officialIds: Set<string>): string | undefined {
|
|
508
|
-
const match = CLAUDE_FAMILY_ALIAS_PATTERN.exec(candidate);
|
|
509
|
-
if (!match?.[1]) {
|
|
510
|
-
return undefined;
|
|
511
|
-
}
|
|
512
|
-
const familyPrefix = match[1].toLowerCase();
|
|
513
|
-
const familyPrefixWithDash = `${familyPrefix}-`;
|
|
514
|
-
|
|
515
|
-
let best: string | undefined;
|
|
516
|
-
let bestVersion: number[] = [];
|
|
517
|
-
let bestHasDate = false;
|
|
518
|
-
let bestHasMarker = false;
|
|
519
|
-
|
|
520
|
-
for (const officialId of officialIds) {
|
|
521
|
-
const normalized = officialId.toLowerCase();
|
|
522
|
-
if (normalized !== familyPrefix && !normalized.startsWith(familyPrefixWithDash)) {
|
|
523
|
-
continue;
|
|
524
|
-
}
|
|
525
|
-
const version = parseClaudeFamilyVersionSegments(officialId, familyPrefix);
|
|
526
|
-
const hasDate = CLAUDE_DATE_SUFFIX_PATTERN.test(officialId);
|
|
527
|
-
const hasMarker = hasTrailingMarker(officialId);
|
|
528
|
-
|
|
529
|
-
if (best === undefined) {
|
|
530
|
-
best = officialId;
|
|
531
|
-
bestVersion = version;
|
|
532
|
-
bestHasDate = hasDate;
|
|
533
|
-
bestHasMarker = hasMarker;
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
const versionDiff = compareVersionSegments(version, bestVersion);
|
|
538
|
-
if (versionDiff !== 0) {
|
|
539
|
-
if (versionDiff > 0) {
|
|
540
|
-
best = officialId;
|
|
541
|
-
bestVersion = version;
|
|
542
|
-
bestHasDate = hasDate;
|
|
543
|
-
bestHasMarker = hasMarker;
|
|
544
|
-
}
|
|
545
|
-
continue;
|
|
546
|
-
}
|
|
547
|
-
if (hasDate !== bestHasDate) {
|
|
548
|
-
if (!hasDate) {
|
|
549
|
-
best = officialId;
|
|
550
|
-
bestVersion = version;
|
|
551
|
-
bestHasDate = hasDate;
|
|
552
|
-
bestHasMarker = hasMarker;
|
|
553
|
-
}
|
|
554
|
-
continue;
|
|
555
|
-
}
|
|
556
|
-
if (hasMarker !== bestHasMarker) {
|
|
557
|
-
if (!hasMarker) {
|
|
558
|
-
best = officialId;
|
|
559
|
-
bestVersion = version;
|
|
560
|
-
bestHasMarker = hasMarker;
|
|
561
|
-
}
|
|
562
|
-
continue;
|
|
563
|
-
}
|
|
564
|
-
if (compareCandidatePreference(officialId, best) < 0) {
|
|
565
|
-
best = officialId;
|
|
566
|
-
bestVersion = version;
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
return best;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
function toggleShortVersionSeparators(candidate: string): string[] {
|
|
573
|
-
const toggled = new Set<string>();
|
|
574
|
-
const dotToDash = candidate.replace(SHORT_VERSION_DOT_TO_DASH_PATTERN, "$1$2-$3");
|
|
575
|
-
if (dotToDash !== candidate) {
|
|
576
|
-
toggled.add(dotToDash);
|
|
577
|
-
}
|
|
578
|
-
const dashToDot = candidate.replace(SHORT_VERSION_DASH_TO_DOT_PATTERN, "$1$2.$3");
|
|
579
|
-
if (dashToDot !== candidate) {
|
|
580
|
-
toggled.add(dashToDot);
|
|
581
|
-
}
|
|
582
|
-
return [...toggled];
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
function expandCompactMinorVersions(candidate: string): string[] {
|
|
586
|
-
const expanded = new Set<string>();
|
|
587
|
-
const compactToDash = candidate.replace(EXPAND_COMPACT_MINOR_PATTERN, "$1$2-$3");
|
|
588
|
-
if (compactToDash !== candidate) {
|
|
589
|
-
expanded.add(compactToDash);
|
|
590
|
-
}
|
|
591
|
-
const compactToDot = candidate.replace(EXPAND_COMPACT_MINOR_PATTERN, "$1$2.$3");
|
|
592
|
-
if (compactToDot !== candidate) {
|
|
593
|
-
expanded.add(compactToDot);
|
|
594
|
-
}
|
|
595
|
-
return [...expanded];
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
function expandCheapCanonicalCandidates(normalized: string, queue: string[]): void {
|
|
599
|
-
const lowercased = lowercaseCandidate(normalized);
|
|
600
|
-
if (lowercased) {
|
|
601
|
-
queue.push(lowercased);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
const pathSegments = normalized.split("/");
|
|
605
|
-
for (let index = 1; index < pathSegments.length; index += 1) {
|
|
606
|
-
queue.push(pathSegments.slice(index).join("/"));
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
for (const suffix of getQualifiedNamespaceSuffixes(normalized)) {
|
|
610
|
-
queue.push(suffix);
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
function expandHeavyCanonicalCandidates(normalized: string, queue: string[]): void {
|
|
615
|
-
for (const toggled of toggleShortVersionSeparators(normalized)) {
|
|
616
|
-
queue.push(toggled);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
const attachedFamilyVersion = insertAttachedFamilyVersionSeparator(normalized);
|
|
620
|
-
if (attachedFamilyVersion) {
|
|
621
|
-
queue.push(attachedFamilyVersion);
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
for (const toggledSeriesVersion of toggleSeriesMinorVersionSeparators(normalized)) {
|
|
625
|
-
queue.push(toggledSeriesVersion);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
for (const expandedVersion of expandCompactMinorVersions(normalized)) {
|
|
629
|
-
queue.push(expandedVersion);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
for (const expandedSeriesVersion of expandCompactSeriesMinorVersions(normalized)) {
|
|
633
|
-
queue.push(expandedSeriesVersion);
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
for (const wrapperCandidate of getWrapperCanonicalCandidates(normalized)) {
|
|
637
|
-
queue.push(wrapperCandidate);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
for (const strippedAffixCandidate of getBracketStrippedModelIdCandidates(normalized)) {
|
|
641
|
-
queue.push(strippedAffixCandidate);
|
|
642
|
-
}
|
|
643
|
-
for (const segment of getModelLikeIdSegments(normalized)) {
|
|
644
|
-
queue.push(segment);
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
const strippedSyntheticPrefix = stripSyntheticPrefix(normalized);
|
|
648
|
-
if (strippedSyntheticPrefix) {
|
|
649
|
-
queue.push(strippedSyntheticPrefix);
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
const strippedLatest = stripLatestSuffix(normalized);
|
|
653
|
-
if (strippedLatest) {
|
|
654
|
-
queue.push(strippedLatest);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
const strippedLegacyGlmTurbo = stripLegacyGlmTurboSuffix(normalized);
|
|
658
|
-
if (strippedLegacyGlmTurbo) {
|
|
659
|
-
queue.push(strippedLegacyGlmTurbo);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
const extractedFamily = extractUpstreamFamilyCandidate(normalized);
|
|
663
|
-
if (extractedFamily) {
|
|
664
|
-
queue.push(extractedFamily);
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
const strippedProviderVersion = stripProviderVersionSuffix(normalized);
|
|
668
|
-
if (strippedProviderVersion) {
|
|
669
|
-
queue.push(strippedProviderVersion);
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
const strippedDate = stripDateSuffix(normalized);
|
|
673
|
-
if (strippedDate) {
|
|
674
|
-
queue.push(strippedDate);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
const strippedMarker = stripTrailingMarker(normalized);
|
|
678
|
-
if (strippedMarker) {
|
|
679
|
-
queue.push(strippedMarker);
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
const reorderedAnthropic = reorderAnthropicFamily(normalized);
|
|
683
|
-
if (reorderedAnthropic) {
|
|
684
|
-
queue.push(reorderedAnthropic);
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// Bounded FIFO memo: result depends only on `modelId` (the `_officialIds` param
|
|
689
|
-
// is unused — kept for signature stability). The returned array is consumed via
|
|
690
|
-
// `.filter` at every callsite, so sharing the cached instance is safe.
|
|
691
|
-
const HEURISTIC_CANDIDATES_CACHE = new Map<string, string[]>();
|
|
692
|
-
const HEURISTIC_CANDIDATES_CACHE_CAP = 4096;
|
|
693
|
-
function getHeuristicCanonicalCandidates(modelId: string, _officialIds?: ReadonlySet<string>): string[] {
|
|
694
|
-
const cached = HEURISTIC_CANDIDATES_CACHE.get(modelId);
|
|
695
|
-
if (cached !== undefined) {
|
|
696
|
-
return cached;
|
|
697
|
-
}
|
|
698
|
-
const candidates = new Set<string>();
|
|
699
|
-
const queue: string[] = [modelId];
|
|
700
|
-
const visited = new Set<string>();
|
|
701
|
-
|
|
702
|
-
for (let qi = 0; qi < queue.length; qi += 1) {
|
|
703
|
-
const candidate = queue[qi];
|
|
704
|
-
if (!candidate) {
|
|
705
|
-
continue;
|
|
706
|
-
}
|
|
707
|
-
const normalized = candidate.trim();
|
|
708
|
-
if (!normalized || visited.has(normalized)) {
|
|
709
|
-
continue;
|
|
710
|
-
}
|
|
711
|
-
visited.add(normalized);
|
|
712
|
-
addCanonicalCandidate(candidates, normalized);
|
|
713
|
-
expandCheapCanonicalCandidates(normalized, queue);
|
|
714
|
-
expandHeavyCanonicalCandidates(normalized, queue);
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
const output = [...candidates];
|
|
718
|
-
if (HEURISTIC_CANDIDATES_CACHE.size >= HEURISTIC_CANDIDATES_CACHE_CAP) {
|
|
719
|
-
const oldest = HEURISTIC_CANDIDATES_CACHE.keys().next().value;
|
|
720
|
-
if (oldest !== undefined) {
|
|
721
|
-
HEURISTIC_CANDIDATES_CACHE.delete(oldest);
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
HEURISTIC_CANDIDATES_CACHE.set(modelId, output);
|
|
725
|
-
return output;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
function getPreferredFallbackCanonicalCandidate(modelId: string, candidates: readonly string[]): string | undefined {
|
|
729
|
-
if (!/[/:.]/.test(modelId)) {
|
|
730
|
-
return undefined;
|
|
731
|
-
}
|
|
732
|
-
const cleanCandidates = candidates.filter(candidate => {
|
|
733
|
-
if (!candidate || candidate === modelId) {
|
|
734
|
-
return false;
|
|
735
|
-
}
|
|
736
|
-
if (candidate.includes("/") || candidate.includes(":")) {
|
|
737
|
-
return false;
|
|
738
|
-
}
|
|
739
|
-
if (candidate.toLowerCase() !== candidate) {
|
|
740
|
-
return false;
|
|
741
|
-
}
|
|
742
|
-
const extractedFamily = extractUpstreamFamilyCandidate(candidate);
|
|
743
|
-
return extractedFamily?.toLowerCase() === candidate;
|
|
744
|
-
});
|
|
745
|
-
return selectBestOfficialCandidate(cleanCandidates);
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
function resolveCanonicalIdForModel(
|
|
749
|
-
model: Model<Api>,
|
|
750
|
-
selector: string,
|
|
751
|
-
equivalence: CompiledEquivalenceConfig,
|
|
752
|
-
referenceData: CanonicalReferenceData,
|
|
753
|
-
): ResolvedCanonicalModel {
|
|
754
|
-
const normalizedSelector = normalizeSelectorKey(selector);
|
|
755
|
-
|
|
756
|
-
if (equivalence.overrides.has(normalizedSelector)) {
|
|
757
|
-
return { id: equivalence.overrides.get(normalizedSelector)!, source: "override" };
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
if (equivalence.exclude.has(normalizedSelector)) {
|
|
761
|
-
return { id: model.id, source: "fallback" };
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
const anthropicAlias = getAnthropicAliasOfficial(model.id, referenceData.officialIds);
|
|
765
|
-
if (anthropicAlias) {
|
|
766
|
-
return { id: anthropicAlias, source: anthropicAlias === model.id ? "bundled" : "heuristic" };
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
const claudeFamilyAlias = getClaudeFamilyAliasOfficial(model.id, referenceData.officialIds);
|
|
770
|
-
if (claudeFamilyAlias) {
|
|
771
|
-
return { id: claudeFamilyAlias, source: claudeFamilyAlias === model.id ? "bundled" : "heuristic" };
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
const heuristicCandidates = getHeuristicCanonicalCandidates(model.id, referenceData.officialIds);
|
|
775
|
-
const officialMatches = new Set<string>();
|
|
776
|
-
for (const candidate of heuristicCandidates) {
|
|
777
|
-
if (referenceData.officialIds.has(candidate)) {
|
|
778
|
-
officialMatches.add(candidate);
|
|
779
|
-
}
|
|
780
|
-
const aliased = referenceData.suffixAliases.get(getCanonicalSuffixAliasKey(candidate));
|
|
781
|
-
if (aliased) {
|
|
782
|
-
officialMatches.add(aliased);
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
const preferredFallback = getPreferredFallbackCanonicalCandidate(model.id, heuristicCandidates);
|
|
786
|
-
const match = selectBestOfficialCandidate([...officialMatches]);
|
|
787
|
-
if (match) {
|
|
788
|
-
if (
|
|
789
|
-
preferredFallback &&
|
|
790
|
-
(match.includes("/") || match.includes(":")) &&
|
|
791
|
-
compareCandidatePreference(preferredFallback, match) < 0
|
|
792
|
-
) {
|
|
793
|
-
return { id: preferredFallback, source: "heuristic" };
|
|
794
|
-
}
|
|
795
|
-
return { id: match, source: match === model.id ? "bundled" : "heuristic" };
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
if (preferredFallback) {
|
|
799
|
-
return { id: preferredFallback, source: "heuristic" };
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
return { id: model.id, source: "fallback" };
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
function getCanonicalRecordName(
|
|
806
|
-
record: CanonicalModelRecord | undefined,
|
|
807
|
-
canonicalId: string,
|
|
808
|
-
variant: CanonicalModelVariant,
|
|
809
|
-
referenceData: CanonicalReferenceData,
|
|
810
|
-
): string {
|
|
811
|
-
if (record) {
|
|
812
|
-
return record.name;
|
|
813
|
-
}
|
|
814
|
-
return referenceData.references.get(canonicalId)?.name ?? variant.model.name ?? canonicalId;
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
function compareCanonicalRecords(left: CanonicalModelRecord, right: CanonicalModelRecord): number {
|
|
818
|
-
return left.id.localeCompare(right.id);
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
function compareCanonicalVariants(left: CanonicalModelVariant, right: CanonicalModelVariant): number {
|
|
822
|
-
const leftSelector = left.selector;
|
|
823
|
-
const rightSelector = right.selector;
|
|
824
|
-
return leftSelector.localeCompare(rightSelector);
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
export function buildCanonicalModelIndex(
|
|
828
|
-
models: readonly Model<Api>[],
|
|
829
|
-
equivalence?: ModelEquivalenceConfig,
|
|
830
|
-
): CanonicalModelIndex {
|
|
831
|
-
const referenceData = createCanonicalReferenceData();
|
|
832
|
-
const compiledEquivalence = compileEquivalenceConfig(equivalence);
|
|
833
|
-
const byId = new Map<string, CanonicalModelRecord>();
|
|
834
|
-
const bySelector = new Map<string, string>();
|
|
835
|
-
|
|
836
|
-
const compiledWithCache = compiledEquivalence as CompiledEquivalenceConfigWithCache;
|
|
837
|
-
let modelCache = compiledWithCache[kModelResolutionCache];
|
|
838
|
-
if (!modelCache) {
|
|
839
|
-
modelCache = new Map<string, ResolvedCanonicalModel>();
|
|
840
|
-
compiledWithCache[kModelResolutionCache] = modelCache;
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
for (const model of models) {
|
|
844
|
-
const selector = formatCanonicalVariantSelector(model);
|
|
845
|
-
const cacheKey = getModelResolutionCacheKey(model);
|
|
846
|
-
let canonical = modelCache.get(cacheKey);
|
|
847
|
-
if (!canonical) {
|
|
848
|
-
canonical = resolveCanonicalIdForModel(model, selector, compiledEquivalence, referenceData);
|
|
849
|
-
modelCache.set(cacheKey, canonical);
|
|
850
|
-
}
|
|
851
|
-
const variant: CanonicalModelVariant = {
|
|
852
|
-
canonicalId: canonical.id,
|
|
853
|
-
selector,
|
|
854
|
-
model,
|
|
855
|
-
source: canonical.source,
|
|
856
|
-
};
|
|
857
|
-
const canonicalKey = normalizeCanonicalIdKey(canonical.id);
|
|
858
|
-
const existing = byId.get(canonicalKey);
|
|
859
|
-
const nextRecord: CanonicalModelRecord = existing ?? {
|
|
860
|
-
id: canonical.id,
|
|
861
|
-
name: getCanonicalRecordName(undefined, canonical.id, variant, referenceData),
|
|
862
|
-
variants: [],
|
|
863
|
-
};
|
|
864
|
-
nextRecord.variants.push(variant);
|
|
865
|
-
byId.set(canonicalKey, nextRecord);
|
|
866
|
-
bySelector.set(normalizeSelectorKey(selector), canonical.id);
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
const records = [...byId.values()].sort(compareCanonicalRecords);
|
|
870
|
-
for (const record of records) {
|
|
871
|
-
record.variants.sort(compareCanonicalVariants);
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
return { records, byId, bySelector };
|
|
875
|
-
}
|