@rigelbuild/omp-catalog 18.0.3
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 +1204 -0
- package/LICENSE +22 -0
- package/README.md +43 -0
- package/THIRD-PARTY-NOTICES.txt +22909 -0
- package/dist/types/build.d.ts +19 -0
- package/dist/types/compat/anthropic.d.ts +18 -0
- package/dist/types/compat/apply.d.ts +7 -0
- package/dist/types/compat/bedrock.d.ts +3 -0
- package/dist/types/compat/devin.d.ts +9 -0
- package/dist/types/compat/openai.d.ts +28 -0
- package/dist/types/discovery/antigravity.d.ts +70 -0
- package/dist/types/discovery/codex.d.ts +36 -0
- package/dist/types/discovery/cursor-proto.d.ts +5368 -0
- package/dist/types/discovery/cursor.d.ts +23 -0
- package/dist/types/discovery/devin-proto.d.ts +1549 -0
- package/dist/types/discovery/devin.d.ts +24 -0
- package/dist/types/discovery/gemini-cli.d.ts +33 -0
- package/dist/types/discovery/gemini.d.ts +25 -0
- package/dist/types/discovery/gitlab-duo-workflow.d.ts +30 -0
- package/dist/types/discovery/index.d.ts +7 -0
- package/dist/types/discovery/openai-compatible.d.ts +89 -0
- package/dist/types/discovery/protobuf.d.ts +102 -0
- package/dist/types/effort.d.ts +10 -0
- package/dist/types/fireworks-model-id.d.ts +23 -0
- package/dist/types/hosts.d.ts +169 -0
- package/dist/types/identity/bundled.d.ts +3 -0
- package/dist/types/identity/classify.d.ts +69 -0
- package/dist/types/identity/dialect.d.ts +3 -0
- package/dist/types/identity/family.d.ts +239 -0
- package/dist/types/identity/id.d.ts +12 -0
- package/dist/types/identity/index.d.ts +8 -0
- package/dist/types/identity/markers.d.ts +4 -0
- package/dist/types/identity/priority.d.ts +1 -0
- package/dist/types/identity/reference.d.ts +29 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/types/model-cache.d.ts +23 -0
- package/dist/types/model-manager.d.ts +77 -0
- package/dist/types/model-thinking.d.ts +70 -0
- package/dist/types/model-tokenizer.d.ts +8 -0
- package/dist/types/models.d.ts +14 -0
- package/dist/types/openai-pricing.d.ts +17 -0
- package/dist/types/provider-models/bundled-references.d.ts +13 -0
- package/dist/types/provider-models/cache-provider-id.d.ts +11 -0
- package/dist/types/provider-models/descriptor-types.d.ts +76 -0
- package/dist/types/provider-models/descriptors.d.ts +505 -0
- package/dist/types/provider-models/google.d.ts +29 -0
- package/dist/types/provider-models/index.d.ts +7 -0
- package/dist/types/provider-models/ollama.d.ts +22 -0
- package/dist/types/provider-models/openai-compat.d.ts +646 -0
- package/dist/types/provider-models/special.d.ts +53 -0
- package/dist/types/types.d.ts +876 -0
- package/dist/types/utils.d.ts +20 -0
- package/dist/types/variant-collapse.d.ts +137 -0
- package/dist/types/wire/alibaba-token-plan.d.ts +22 -0
- package/dist/types/wire/codex.d.ts +66 -0
- package/dist/types/wire/coreweave.d.ts +12 -0
- package/dist/types/wire/gemini-headers.d.ts +62 -0
- package/dist/types/wire/github-copilot.d.ts +47 -0
- package/dist/types/wire/image-fetchers.d.ts +76 -0
- package/package.json +102 -0
- package/src/build.ts +103 -0
- package/src/compat/anthropic.ts +179 -0
- package/src/compat/apply.ts +15 -0
- package/src/compat/bedrock.ts +153 -0
- package/src/compat/devin.ts +12 -0
- package/src/compat/openai.ts +864 -0
- package/src/discovery/antigravity.ts +257 -0
- package/src/discovery/codex.ts +456 -0
- package/src/discovery/cursor-proto.ts +8329 -0
- package/src/discovery/cursor.ts +409 -0
- package/src/discovery/devin-proto.ts +2037 -0
- package/src/discovery/devin.ts +152 -0
- package/src/discovery/gemini-cli.ts +198 -0
- package/src/discovery/gemini.ts +260 -0
- package/src/discovery/gitlab-duo-workflow.ts +859 -0
- package/src/discovery/index.ts +7 -0
- package/src/discovery/openai-compatible.ts +266 -0
- package/src/discovery/protobuf.ts +1073 -0
- package/src/effort.ts +18 -0
- package/src/fireworks-model-id.ts +50 -0
- package/src/hosts.ts +178 -0
- package/src/identity/bundled.ts +28 -0
- package/src/identity/classify.ts +242 -0
- package/src/identity/dialect.ts +42 -0
- package/src/identity/family.ts +477 -0
- package/src/identity/id.ts +81 -0
- package/src/identity/index.ts +8 -0
- package/src/identity/markers.ts +49 -0
- package/src/identity/priority.ts +59 -0
- package/src/identity/reference.ts +167 -0
- package/src/index.ts +19 -0
- package/src/model-cache.ts +346 -0
- package/src/model-manager.ts +683 -0
- package/src/model-thinking.ts +925 -0
- package/src/model-tokenizer.ts +75 -0
- package/src/models.json +304657 -0
- package/src/models.json.d.ts +9 -0
- package/src/models.ts +108 -0
- package/src/openai-pricing.ts +29 -0
- package/src/provider-models/bundled-references.ts +87 -0
- package/src/provider-models/cache-provider-id.ts +97 -0
- package/src/provider-models/descriptor-types.ts +85 -0
- package/src/provider-models/descriptors.ts +585 -0
- package/src/provider-models/google.ts +117 -0
- package/src/provider-models/index.ts +7 -0
- package/src/provider-models/ollama.ts +204 -0
- package/src/provider-models/openai-compat.ts +6405 -0
- package/src/provider-models/special.ts +213 -0
- package/src/types.ts +1032 -0
- package/src/utils.ts +69 -0
- package/src/variant-collapse.ts +1555 -0
- package/src/wire/alibaba-token-plan.ts +54 -0
- package/src/wire/codex.ts +117 -0
- package/src/wire/coreweave.ts +51 -0
- package/src/wire/gemini-headers.ts +135 -0
- package/src/wire/github-copilot.ts +147 -0
- package/src/wire/image-fetchers.ts +159 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Api } from "./types";
|
|
2
|
+
|
|
3
|
+
/** Typed declaration for the generated models.json — only the `api` field matters for type inference. */
|
|
4
|
+
declare const models: {
|
|
5
|
+
[provider: string]: {
|
|
6
|
+
[modelId: string]: { readonly api: Api; [key: string]: unknown };
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export default models;
|
package/src/models.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import MODELS from "./models.json" with { type: "json" };
|
|
2
|
+
import type { Api, KnownProvider, Model, TokenCost, Usage } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Static bundled model registry loaded from `models.json`.
|
|
6
|
+
*
|
|
7
|
+
* This module intentionally exposes compile-time defaults only.
|
|
8
|
+
* It does not include runtime discovery, stencil.so overlays, or on-disk cache state.
|
|
9
|
+
*
|
|
10
|
+
* For runtime-aware resolution, use `createModelManager()` / `resolveProviderModels()`.
|
|
11
|
+
*/
|
|
12
|
+
const modelRegistry = new Map<string, Map<string, Model<Api>>>();
|
|
13
|
+
|
|
14
|
+
/** Return one provider's bundled models, materialized by the generator. */
|
|
15
|
+
function getProviderModels(provider: string): Map<string, Model<Api>> | undefined {
|
|
16
|
+
const cachedModels = modelRegistry.get(provider);
|
|
17
|
+
if (cachedModels !== undefined) return cachedModels;
|
|
18
|
+
if (!Object.hasOwn(MODELS, provider)) return undefined;
|
|
19
|
+
|
|
20
|
+
const providerModels = new Map<string, Model<Api>>();
|
|
21
|
+
const rawModels = MODELS[provider as keyof typeof MODELS];
|
|
22
|
+
for (const id in rawModels) {
|
|
23
|
+
// models.json rows are complete Models emitted by generate-models.ts;
|
|
24
|
+
// consuming them verbatim keeps startup allocation-free.
|
|
25
|
+
providerModels.set(id, rawModels[id as keyof typeof rawModels] as unknown as Model<Api>);
|
|
26
|
+
}
|
|
27
|
+
modelRegistry.set(provider, providerModels);
|
|
28
|
+
return providerModels;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type GeneratedProvider = keyof typeof MODELS;
|
|
32
|
+
|
|
33
|
+
export function getBundledModel<TApi extends Api = Api>(provider: GeneratedProvider, modelId: string): Model<TApi> {
|
|
34
|
+
const providerModels = getProviderModels(provider);
|
|
35
|
+
return providerModels?.get(modelId) as Model<TApi>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getBundledProviders(): KnownProvider[] {
|
|
39
|
+
return Object.keys(MODELS) as KnownProvider[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getBundledModels(provider: GeneratedProvider): Model<Api>[] {
|
|
43
|
+
const models = getProviderModels(provider);
|
|
44
|
+
return models ? (Array.from(models.values()) as Model<Api>[]) : [];
|
|
45
|
+
}
|
|
46
|
+
function resolveTokenCost(cost: Model["cost"], promptInputTokens: number): TokenCost {
|
|
47
|
+
const longContext = cost.longContext;
|
|
48
|
+
if (!longContext) return cost;
|
|
49
|
+
return promptInputTokens > longContext.inputThreshold ? longContext : cost;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Price a prompt as fully uncached input under its active context-length tier. */
|
|
53
|
+
export function calculateUncachedInputCost(cost: Model["cost"], promptInputTokens: number): number {
|
|
54
|
+
const rates = resolveTokenCost(cost, promptInputTokens);
|
|
55
|
+
return (rates.input / 1_000_000) * promptInputTokens;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function calculateCost<TApi extends Api>(model: Model<TApi>, usage: Usage): Usage["cost"] {
|
|
59
|
+
const orchestration = usage.orchestration;
|
|
60
|
+
const promptInputTokens =
|
|
61
|
+
usage.input + usage.cacheRead + usage.cacheWrite + (orchestration?.input ?? 0) + (orchestration?.cacheRead ?? 0);
|
|
62
|
+
const rates = resolveTokenCost(model.cost, promptInputTokens);
|
|
63
|
+
usage.cost.input = (rates.input / 1000000) * (usage.input + (orchestration?.input ?? 0));
|
|
64
|
+
usage.cost.output = (rates.output / 1000000) * (usage.output + (orchestration?.output ?? 0));
|
|
65
|
+
usage.cost.cacheRead = (rates.cacheRead / 1000000) * (usage.cacheRead + (orchestration?.cacheRead ?? 0));
|
|
66
|
+
usage.cost.cacheWrite = cacheWriteCost(rates, usage);
|
|
67
|
+
usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
|
|
68
|
+
return usage.cost;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Price cache-write tokens, honoring the TTL breakdown when the provider reports one.
|
|
73
|
+
*
|
|
74
|
+
* `rates.cacheWrite` is the 5-minute write rate (Anthropic bills 5m writes at
|
|
75
|
+
* 1.25x base input). When `usage.cttl` is present the write can mix 5m and 1h
|
|
76
|
+
* breakpoints, and 1h writes bill at 2x base input, so each component is
|
|
77
|
+
* priced at its own rate instead of the flat 5m rate. Deriving 1h from
|
|
78
|
+
* `input * 2` (Anthropic's published multiplier) is model-independent and
|
|
79
|
+
* stays correct even for legacy entries whose stored
|
|
80
|
+
* `cacheWrite` scalar drifts from 1.25x input. Providers that omit `cttl`
|
|
81
|
+
* (everyone but Anthropic) keep the flat-rate calculation.
|
|
82
|
+
*
|
|
83
|
+
* The breakdown is documented to sum to `usage.cacheWrite`, but the two are written
|
|
84
|
+
* from independent wire fields (`cache_creation` vs `cache_creation_input_tokens`),
|
|
85
|
+
* so any unattributed remainder is priced at the flat rate instead of being dropped:
|
|
86
|
+
* a partial or stale breakdown must never make write tokens free.
|
|
87
|
+
*/
|
|
88
|
+
function cacheWriteCost(rates: TokenCost, usage: Usage): number {
|
|
89
|
+
const rate5m = rates.cacheWrite / 1000000;
|
|
90
|
+
const cttl = usage.cttl;
|
|
91
|
+
if (!cttl) return rate5m * usage.cacheWrite;
|
|
92
|
+
const fiveMinute = cttl.ephemeral5m ?? 0;
|
|
93
|
+
const oneHour = cttl.ephemeral1h ?? 0;
|
|
94
|
+
const residual = Math.max(0, usage.cacheWrite - fiveMinute - oneHour);
|
|
95
|
+
return rate5m * (fiveMinute + residual) + ((rates.input * 2) / 1000000) * oneHour;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check if two models are equal by comparing both their id and provider.
|
|
100
|
+
* Returns false if either model is null or undefined.
|
|
101
|
+
*/
|
|
102
|
+
export function modelsAreEqual<TApi extends Api>(
|
|
103
|
+
a: Model<TApi> | null | undefined,
|
|
104
|
+
b: Model<TApi> | null | undefined,
|
|
105
|
+
): boolean {
|
|
106
|
+
if (!a || !b) return false;
|
|
107
|
+
return a.id === b.id && a.provider === b.provider;
|
|
108
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { TokenCost } from "./types";
|
|
2
|
+
|
|
3
|
+
/** Standard GPT-5.6 Sol rates used by the Daybreak Blue aliases. */
|
|
4
|
+
export const OPENAI_GPT_56_SOL_STANDARD_COST = {
|
|
5
|
+
input: 5,
|
|
6
|
+
output: 30,
|
|
7
|
+
cacheRead: 0.5,
|
|
8
|
+
cacheWrite: 6.25,
|
|
9
|
+
} as const satisfies TokenCost;
|
|
10
|
+
|
|
11
|
+
/** Standard GPT-5.6 Cyber rates used by the Daybreak Red aliases. */
|
|
12
|
+
export const OPENAI_GPT_56_CYBER_STANDARD_COST = {
|
|
13
|
+
input: 12.5,
|
|
14
|
+
output: 75,
|
|
15
|
+
cacheRead: 1.25,
|
|
16
|
+
cacheWrite: 15.625,
|
|
17
|
+
} as const satisfies TokenCost;
|
|
18
|
+
|
|
19
|
+
/** Resolve standard rates for Codex-prefixed Daybreak aliases. */
|
|
20
|
+
export function resolveOpenAIDaybreakStandardCost(modelId: string): TokenCost | undefined {
|
|
21
|
+
switch (modelId) {
|
|
22
|
+
case "gpt-daybreak-blue-latest":
|
|
23
|
+
return OPENAI_GPT_56_SOL_STANDARD_COST;
|
|
24
|
+
case "gpt-daybreak-red-latest":
|
|
25
|
+
return OPENAI_GPT_56_CYBER_STANDARD_COST;
|
|
26
|
+
default:
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { isZeroCostXaiOAuthReference } from "../identity/reference";
|
|
2
|
+
import { getBundledModels, getBundledProviders } from "../models";
|
|
3
|
+
import type { Api, Model, ModelSpec } from "../types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Project a built `Model` back to spec stage: `compat` becomes the verbatim
|
|
7
|
+
* sparse override record (`compatConfig`), never the resolved view. Discovery
|
|
8
|
+
* mappers spread these references into the specs they hand to the model
|
|
9
|
+
* manager, which rebuilds via `buildModel`.
|
|
10
|
+
*/
|
|
11
|
+
export function toModelSpec<TApi extends Api>(model: Model<TApi>): ModelSpec<TApi> {
|
|
12
|
+
const {
|
|
13
|
+
compat: _compat,
|
|
14
|
+
compatConfig,
|
|
15
|
+
supportsComputerUse: _derivedComputerUse,
|
|
16
|
+
supportsComputerUseConfig,
|
|
17
|
+
...rest
|
|
18
|
+
} = model;
|
|
19
|
+
return {
|
|
20
|
+
...rest,
|
|
21
|
+
...(supportsComputerUseConfig !== undefined ? { supportsComputerUse: supportsComputerUseConfig } : {}),
|
|
22
|
+
compat: compatConfig,
|
|
23
|
+
} as ModelSpec<TApi>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createBundledReferenceMap<TApi extends Api>(
|
|
27
|
+
provider: Parameters<typeof getBundledModels>[0],
|
|
28
|
+
): Map<string, ModelSpec<TApi>> {
|
|
29
|
+
const references = new Map<string, ModelSpec<TApi>>();
|
|
30
|
+
for (const model of getBundledModels(provider)) {
|
|
31
|
+
references.set(model.id, toModelSpec(model as Model<TApi>));
|
|
32
|
+
}
|
|
33
|
+
return references;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type ProviderReferenceSource<TApi extends Api> = Map<string, ModelSpec<TApi>> | (() => Map<string, ModelSpec<TApi>>);
|
|
37
|
+
|
|
38
|
+
let globalReferences: Map<string, Model<Api>> | undefined;
|
|
39
|
+
|
|
40
|
+
function getGlobalReferences(): Map<string, Model<Api>> {
|
|
41
|
+
if (globalReferences) {
|
|
42
|
+
return globalReferences;
|
|
43
|
+
}
|
|
44
|
+
const references = new Map<string, Model<Api>>();
|
|
45
|
+
for (const provider of getBundledProviders()) {
|
|
46
|
+
for (const model of getBundledModels(provider as Parameters<typeof getBundledModels>[0])) {
|
|
47
|
+
const candidate = model as Model<Api>;
|
|
48
|
+
if (isZeroCostXaiOAuthReference(candidate)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const existing = references.get(candidate.id);
|
|
52
|
+
if (!existing) {
|
|
53
|
+
references.set(candidate.id, candidate);
|
|
54
|
+
} else if (candidate.contextWindow !== existing.contextWindow) {
|
|
55
|
+
if ((candidate.contextWindow ?? 0) > (existing.contextWindow ?? 0)) {
|
|
56
|
+
references.set(candidate.id, candidate);
|
|
57
|
+
}
|
|
58
|
+
} else if (candidate.maxTokens !== existing.maxTokens) {
|
|
59
|
+
if ((candidate.maxTokens ?? 0) > (existing.maxTokens ?? 0)) {
|
|
60
|
+
references.set(candidate.id, candidate);
|
|
61
|
+
}
|
|
62
|
+
} else if (existing.provider !== "openai" && candidate.provider === "openai") {
|
|
63
|
+
references.set(candidate.id, candidate);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
globalReferences = references;
|
|
68
|
+
return references;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function createReferenceResolver<TApi extends Api>(
|
|
72
|
+
providerReferenceSource: ProviderReferenceSource<TApi>,
|
|
73
|
+
): (modelId: string) => ModelSpec<TApi> | undefined {
|
|
74
|
+
let lazyProviderReferences: Map<string, ModelSpec<TApi>> | undefined;
|
|
75
|
+
const getProviderReferences =
|
|
76
|
+
typeof providerReferenceSource === "function"
|
|
77
|
+
? () => (lazyProviderReferences ??= providerReferenceSource())
|
|
78
|
+
: () => providerReferenceSource;
|
|
79
|
+
return (modelId: string) => {
|
|
80
|
+
const providerRefs = getProviderReferences();
|
|
81
|
+
const globalRefs = getGlobalReferences();
|
|
82
|
+
const providerRef = providerRefs.get(modelId);
|
|
83
|
+
if (providerRef) return providerRef;
|
|
84
|
+
const globalRef = globalRefs.get(modelId);
|
|
85
|
+
return globalRef ? toModelSpec(globalRef as Model<TApi>) : undefined;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { PERSONAL_GITHUB_COPILOT_BASE_URL } from "../wire/github-copilot";
|
|
2
|
+
|
|
3
|
+
export interface ModelCacheProviderIdOptions {
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const CREDENTIAL_SCOPED_MODEL_CACHE_PROVIDERS: Readonly<Record<string, true>> = {
|
|
9
|
+
"opencode-go": true,
|
|
10
|
+
"opencode-zen": true,
|
|
11
|
+
"github-copilot": true,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/** Whether a provider's model-cache namespace requires its resolved credential. */
|
|
15
|
+
export function isCredentialScopedModelCacheProvider(providerId: string): boolean {
|
|
16
|
+
return CREDENTIAL_SCOPED_MODEL_CACHE_PROVIDERS[providerId] === true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getDefaultModelDiscoveryBaseUrl(providerId: string): string | undefined {
|
|
20
|
+
switch (providerId) {
|
|
21
|
+
case "ollama":
|
|
22
|
+
return "http://127.0.0.1:11434";
|
|
23
|
+
case "litellm":
|
|
24
|
+
return Bun.env.LITELLM_BASE_URL ?? "http://localhost:4000/v1";
|
|
25
|
+
case "opencode-go":
|
|
26
|
+
return "https://opencode.ai/zen/go/v1";
|
|
27
|
+
case "opencode-zen":
|
|
28
|
+
return "https://opencode.ai/zen/v1";
|
|
29
|
+
case "vllm":
|
|
30
|
+
return "http://127.0.0.1:8000/v1";
|
|
31
|
+
default:
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Resolve an Ollama model-cache namespace scoped to the normalized discovery endpoint. */
|
|
37
|
+
export function resolveOllamaModelCacheProviderId(providerId: string, baseUrl?: string): string {
|
|
38
|
+
const defaultBaseUrl = getDefaultModelDiscoveryBaseUrl("ollama")!;
|
|
39
|
+
let endpoint = defaultBaseUrl;
|
|
40
|
+
try {
|
|
41
|
+
const parsed = new URL(baseUrl ?? defaultBaseUrl);
|
|
42
|
+
const trimmedPath = parsed.pathname.replace(/\/+$/g, "");
|
|
43
|
+
const nativePath = trimmedPath.endsWith("/v1") ? trimmedPath.slice(0, -3) : trimmedPath;
|
|
44
|
+
endpoint = `${parsed.protocol}//${parsed.host}${nativePath}`;
|
|
45
|
+
} catch {
|
|
46
|
+
// Malformed URLs fall back during discovery, so share the default endpoint's cache.
|
|
47
|
+
}
|
|
48
|
+
return `${providerId}:ollama-models-v1:${Bun.hash(endpoint).toString(36)}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Resolve the cache namespace used by a provider's model-manager options without constructing those options. */
|
|
52
|
+
export function resolveModelCacheProviderId(providerId: string, options: ModelCacheProviderIdOptions = {}): string {
|
|
53
|
+
switch (providerId) {
|
|
54
|
+
case "ollama":
|
|
55
|
+
return resolveOllamaModelCacheProviderId(providerId, options.baseUrl);
|
|
56
|
+
case "cursor":
|
|
57
|
+
// v3: max-mode Claude/Gemini rows cached before the 1M context-window
|
|
58
|
+
// discovery fix carry a stale 200k window and must be refetched.
|
|
59
|
+
return "cursor:max-mode-v3";
|
|
60
|
+
case "litellm": {
|
|
61
|
+
const baseUrl = options.baseUrl ?? getDefaultModelDiscoveryBaseUrl(providerId)!;
|
|
62
|
+
return `litellm:rich-v6:${Bun.hash(baseUrl).toString(36)}`;
|
|
63
|
+
}
|
|
64
|
+
case "opencode-go":
|
|
65
|
+
case "opencode-zen": {
|
|
66
|
+
// v3: gateway-first rows cached before stencil enrichment carry null
|
|
67
|
+
// limits and `reasoning: false`; use a fresh namespace so they refetch.
|
|
68
|
+
const configuredBaseUrl = options.baseUrl ?? getDefaultModelDiscoveryBaseUrl(providerId)!;
|
|
69
|
+
const trimmedBaseUrl = configuredBaseUrl.endsWith("/") ? configuredBaseUrl.slice(0, -1) : configuredBaseUrl;
|
|
70
|
+
const discoveryBaseUrl = trimmedBaseUrl.endsWith("/v1") ? trimmedBaseUrl : `${trimmedBaseUrl}/v1`;
|
|
71
|
+
const scope = `${options.apiKey ?? ""}\u0000${discoveryBaseUrl}`;
|
|
72
|
+
return `${providerId}:models-v3:${Bun.hash(scope).toString(36)}`;
|
|
73
|
+
}
|
|
74
|
+
case "github-copilot": {
|
|
75
|
+
// Copilot model specs bake in the plan-specific endpoint (personal vs
|
|
76
|
+
// Business/Enterprise) resolved from the credential. Discovery writes an
|
|
77
|
+
// authoritative cache, so `online-if-uncached` serves it for the full
|
|
78
|
+
// TTL without re-probing. Keying the namespace on the credential means
|
|
79
|
+
// switching `COPILOT_GITHUB_TOKEN` to a different account misses the
|
|
80
|
+
// prior endpoint's cache and re-runs discovery instead of hitting the
|
|
81
|
+
// stale host and 403ing (PR #8510 review).
|
|
82
|
+
const baseUrl = options.baseUrl ?? PERSONAL_GITHUB_COPILOT_BASE_URL;
|
|
83
|
+
const scope = `${options.apiKey ?? ""}\u0000${baseUrl}`;
|
|
84
|
+
return `github-copilot:models-v1:${Bun.hash(scope).toString(36)}`;
|
|
85
|
+
}
|
|
86
|
+
case "openrouter":
|
|
87
|
+
return "openrouter:pseudo-api";
|
|
88
|
+
case "vllm": {
|
|
89
|
+
// v2: qwen3.8 rows cached before the reasoning/template-effort upgrade
|
|
90
|
+
// carry `reasoning: false` and must be refetched.
|
|
91
|
+
const baseUrl = options.baseUrl ?? getDefaultModelDiscoveryBaseUrl(providerId)!;
|
|
92
|
+
return `vllm:models-v2:${Bun.hash(baseUrl).toString(36)}`;
|
|
93
|
+
}
|
|
94
|
+
default:
|
|
95
|
+
return providerId;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { ModelManagerOptions } from "../model-manager";
|
|
2
|
+
import type { Api, FetchImpl } from "../types";
|
|
3
|
+
|
|
4
|
+
/** Config passed to a provider's runtime model-manager factory. */
|
|
5
|
+
export type ModelManagerConfig = {
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
fetch?: FetchImpl;
|
|
9
|
+
/** The supplied fetch already applies provider-specific authentication. */
|
|
10
|
+
authenticated?: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** Catalog discovery configuration for providers that support endpoint-based model listing. */
|
|
14
|
+
export interface CatalogDiscoveryConfig {
|
|
15
|
+
/** Human-readable name for log messages. */
|
|
16
|
+
label: string;
|
|
17
|
+
/**
|
|
18
|
+
* Environment variables to check for API keys during catalog generation.
|
|
19
|
+
* Defaults to the entry-level `envVars` when omitted.
|
|
20
|
+
*/
|
|
21
|
+
envVars?: readonly string[];
|
|
22
|
+
/** OAuth provider for credential refresh during catalog generation. */
|
|
23
|
+
oauthProvider?: string;
|
|
24
|
+
/** When true, catalog discovery proceeds even without credentials. */
|
|
25
|
+
allowUnauthenticated?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Unified provider descriptor used by both runtime discovery and catalog generation. */
|
|
29
|
+
export interface ProviderDescriptor {
|
|
30
|
+
providerId: string;
|
|
31
|
+
createModelManagerOptions(config: ModelManagerConfig): ModelManagerOptions<Api>;
|
|
32
|
+
/** Preferred model ID when no explicit selection is made. */
|
|
33
|
+
defaultModel: string;
|
|
34
|
+
/** When true, the runtime creates a model manager even without a valid API key (e.g. ollama). */
|
|
35
|
+
allowUnauthenticated?: boolean;
|
|
36
|
+
/** When true, successful runtime discovery replaces bundled provider models instead of merging fallback-only IDs. */
|
|
37
|
+
dynamicModelsAuthoritative?: boolean;
|
|
38
|
+
/** Catalog discovery configuration. Only providers with this field participate in generate-models.ts. */
|
|
39
|
+
catalogDiscovery?: CatalogDiscoveryConfig;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A provider descriptor that has catalog discovery configured. */
|
|
43
|
+
export type CatalogProviderDescriptor = ProviderDescriptor & { catalogDiscovery: CatalogDiscoveryConfig };
|
|
44
|
+
|
|
45
|
+
/** Type guard for descriptors with catalog discovery. */
|
|
46
|
+
export function isCatalogDescriptor(d: ProviderDescriptor): d is CatalogProviderDescriptor {
|
|
47
|
+
return d.catalogDiscovery != null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Whether catalog discovery may run without provider credentials. */
|
|
51
|
+
export function allowsUnauthenticatedCatalogDiscovery(descriptor: CatalogProviderDescriptor): boolean {
|
|
52
|
+
return descriptor.catalogDiscovery.allowUnauthenticated ?? descriptor.allowUnauthenticated ?? false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* One model provider's catalog-side description. The auth half of a provider
|
|
57
|
+
* (env keys, OAuth login/refresh flows) lives in `@oh-my-pi/pi-ai`'s registry;
|
|
58
|
+
* the catalog table below is the single source of truth for ids, default
|
|
59
|
+
* models, and discovery wiring.
|
|
60
|
+
*
|
|
61
|
+
* - Every entry is a member of `KnownProvider`.
|
|
62
|
+
* - `createModelManagerOptions` present (and not `specialModelManager`) ⇒
|
|
63
|
+
* appears in `PROVIDER_DESCRIPTORS` for runtime model discovery.
|
|
64
|
+
* - \`catalogDiscovery\` present ⇒ participates in \`generate-models.ts\`.
|
|
65
|
+
*/
|
|
66
|
+
export interface ProviderCatalogEntry {
|
|
67
|
+
readonly id: string;
|
|
68
|
+
/** Preferred model ID when no explicit selection is made. */
|
|
69
|
+
readonly defaultModel: string;
|
|
70
|
+
/** Environment variables consulted (in order) for the provider's runtime API-key env fallback. */
|
|
71
|
+
readonly envVars?: readonly string[];
|
|
72
|
+
/** Runtime model-manager factory. Omitted for catalog-only providers. */
|
|
73
|
+
readonly createModelManagerOptions?: (config: ModelManagerConfig) => ModelManagerOptions<Api>;
|
|
74
|
+
/** When true, the runtime creates a model manager even without a valid API key. */
|
|
75
|
+
readonly allowUnauthenticated?: boolean;
|
|
76
|
+
/** When true, successful runtime discovery replaces bundled provider models. */
|
|
77
|
+
readonly dynamicModelsAuthoritative?: boolean;
|
|
78
|
+
/** Catalog discovery configuration for generate-models.ts. */
|
|
79
|
+
readonly catalogDiscovery?: CatalogDiscoveryConfig;
|
|
80
|
+
/**
|
|
81
|
+
* Built bespoke by the coding-agent runtime (OAuth-token-driven managers);
|
|
82
|
+
* excluded from `PROVIDER_DESCRIPTORS` even though models are discoverable.
|
|
83
|
+
*/
|
|
84
|
+
readonly specialModelManager?: boolean;
|
|
85
|
+
}
|