jeopi-catalog 16.2.13
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 +523 -0
- package/README.md +43 -0
- package/dist/types/build.d.ts +3 -0
- package/dist/types/compat/anthropic.d.ts +11 -0
- package/dist/types/compat/apply.d.ts +7 -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 +38 -0
- package/dist/types/discovery/cursor-gen/agent_pb.d.ts +13022 -0
- package/dist/types/discovery/cursor.d.ts +23 -0
- package/dist/types/discovery/devin-gen/buf/validate/validate_pb.d.ts +1715 -0
- package/dist/types/discovery/devin-gen/exa/analytics_pb/analytics_pb.d.ts +693 -0
- package/dist/types/discovery/devin-gen/exa/api_server_pb/api_server_pb.d.ts +9158 -0
- package/dist/types/discovery/devin-gen/exa/auth_pb/auth_pb.d.ts +52 -0
- package/dist/types/discovery/devin-gen/exa/auto_cascade_common_pb/auto_cascade_common_pb.d.ts +276 -0
- package/dist/types/discovery/devin-gen/exa/bug_checker_pb/bug_checker_pb.d.ts +78 -0
- package/dist/types/discovery/devin-gen/exa/cascade_plugins_pb/cascade_plugins_pb.d.ts +999 -0
- package/dist/types/discovery/devin-gen/exa/chat_pb/chat_pb.d.ts +1704 -0
- package/dist/types/discovery/devin-gen/exa/code_edit/code_edit_pb/code_edit_pb.d.ts +656 -0
- package/dist/types/discovery/devin-gen/exa/codeium_common_pb/codeium_common_pb.d.ts +15014 -0
- package/dist/types/discovery/devin-gen/exa/context_module_pb/context_module_pb.d.ts +607 -0
- package/dist/types/discovery/devin-gen/exa/cortex_pb/cortex_pb.d.ts +11830 -0
- package/dist/types/discovery/devin-gen/exa/diff_action_pb/diff_action_pb.d.ts +248 -0
- package/dist/types/discovery/devin-gen/exa/index_pb/index_pb.d.ts +1747 -0
- package/dist/types/discovery/devin-gen/exa/knowledge_base_pb/knowledge_base_pb.d.ts +509 -0
- package/dist/types/discovery/devin-gen/exa/language_server_pb/language_server_pb.d.ts +9048 -0
- package/dist/types/discovery/devin-gen/exa/opensearch_clients_pb/opensearch_clients_pb.d.ts +1760 -0
- package/dist/types/discovery/devin-gen/exa/prompt_pb/prompt_pb.d.ts +286 -0
- package/dist/types/discovery/devin-gen/exa/reactive_component_pb/reactive_component_pb.d.ts +405 -0
- package/dist/types/discovery/devin-gen/exa/trust_pb/trust_pb.d.ts +582 -0
- package/dist/types/discovery/devin.d.ts +24 -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 +5 -0
- package/dist/types/discovery/openai-compatible.d.ts +74 -0
- package/dist/types/effort.d.ts +9 -0
- package/dist/types/fireworks-model-id.d.ts +23 -0
- package/dist/types/hosts.d.ts +137 -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 +139 -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 +23 -0
- package/dist/types/index.d.ts +17 -0
- package/dist/types/model-cache.d.ts +17 -0
- package/dist/types/model-manager.d.ts +68 -0
- package/dist/types/model-thinking.d.ts +79 -0
- package/dist/types/models.d.ts +12 -0
- package/dist/types/provider-models/bundled-references.d.ts +11 -0
- package/dist/types/provider-models/descriptor-types.d.ts +74 -0
- package/dist/types/provider-models/descriptors.d.ts +427 -0
- package/dist/types/provider-models/google.d.ts +27 -0
- package/dist/types/provider-models/index.d.ts +6 -0
- package/dist/types/provider-models/ollama.d.ts +9 -0
- package/dist/types/provider-models/openai-compat.d.ts +450 -0
- package/dist/types/provider-models/special.d.ts +33 -0
- package/dist/types/types.d.ts +656 -0
- package/dist/types/utils.d.ts +20 -0
- package/dist/types/variant-collapse.d.ts +133 -0
- package/dist/types/wire/codex.d.ts +26 -0
- package/dist/types/wire/coreweave.d.ts +12 -0
- package/dist/types/wire/gemini-headers.d.ts +37 -0
- package/dist/types/wire/github-copilot.d.ts +45 -0
- package/package.json +101 -0
- package/src/build.ts +47 -0
- package/src/compat/anthropic.ts +93 -0
- package/src/compat/apply.ts +15 -0
- package/src/compat/devin.ts +12 -0
- package/src/compat/openai.ts +689 -0
- package/src/discovery/antigravity.ts +253 -0
- package/src/discovery/codex.ts +371 -0
- package/src/discovery/cursor-gen/agent_pb.ts +15274 -0
- package/src/discovery/cursor.ts +314 -0
- package/src/discovery/devin-gen/buf/validate/validate_pb.ts +1862 -0
- package/src/discovery/devin-gen/exa/analytics_pb/analytics_pb.ts +871 -0
- package/src/discovery/devin-gen/exa/api_server_pb/api_server_pb.ts +11083 -0
- package/src/discovery/devin-gen/exa/auth_pb/auth_pb.ts +71 -0
- package/src/discovery/devin-gen/exa/auto_cascade_common_pb/auto_cascade_common_pb.ts +348 -0
- package/src/discovery/devin-gen/exa/bug_checker_pb/bug_checker_pb.ts +103 -0
- package/src/discovery/devin-gen/exa/cascade_plugins_pb/cascade_plugins_pb.ts +1198 -0
- package/src/discovery/devin-gen/exa/chat_pb/chat_pb.ts +2063 -0
- package/src/discovery/devin-gen/exa/code_edit/code_edit_pb/code_edit_pb.ts +810 -0
- package/src/discovery/devin-gen/exa/codeium_common_pb/codeium_common_pb.ts +18354 -0
- package/src/discovery/devin-gen/exa/context_module_pb/context_module_pb.ts +732 -0
- package/src/discovery/devin-gen/exa/cortex_pb/cortex_pb.ts +14277 -0
- package/src/discovery/devin-gen/exa/diff_action_pb/diff_action_pb.ts +312 -0
- package/src/discovery/devin-gen/exa/index_pb/index_pb.ts +2106 -0
- package/src/discovery/devin-gen/exa/knowledge_base_pb/knowledge_base_pb.ts +623 -0
- package/src/discovery/devin-gen/exa/language_server_pb/language_server_pb.ts +10918 -0
- package/src/discovery/devin-gen/exa/opensearch_clients_pb/opensearch_clients_pb.ts +2125 -0
- package/src/discovery/devin-gen/exa/prompt_pb/prompt_pb.ts +361 -0
- package/src/discovery/devin-gen/exa/reactive_component_pb/reactive_component_pb.ts +450 -0
- package/src/discovery/devin-gen/exa/trust_pb/trust_pb.ts +712 -0
- package/src/discovery/devin.ts +151 -0
- package/src/discovery/gemini.ts +260 -0
- package/src/discovery/gitlab-duo-workflow.ts +855 -0
- package/src/discovery/index.ts +5 -0
- package/src/discovery/openai-compatible.ts +249 -0
- package/src/effort.ts +16 -0
- package/src/fireworks-model-id.ts +50 -0
- package/src/hosts.ts +129 -0
- package/src/identity/bundled.ts +28 -0
- package/src/identity/classify.ts +214 -0
- package/src/identity/dialect.ts +42 -0
- package/src/identity/family.ts +296 -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 +57 -0
- package/src/identity/reference.ts +148 -0
- package/src/index.ts +17 -0
- package/src/model-cache.ts +171 -0
- package/src/model-manager.ts +520 -0
- package/src/model-thinking.ts +760 -0
- package/src/models.json +88898 -0
- package/src/models.json.d.ts +9 -0
- package/src/models.ts +64 -0
- package/src/provider-models/bundled-references.ts +58 -0
- package/src/provider-models/descriptor-types.ts +79 -0
- package/src/provider-models/descriptors.ts +503 -0
- package/src/provider-models/google.ts +107 -0
- package/src/provider-models/index.ts +6 -0
- package/src/provider-models/ollama.ts +172 -0
- package/src/provider-models/openai-compat.ts +4283 -0
- package/src/provider-models/special.ts +159 -0
- package/src/types.ts +786 -0
- package/src/utils.ts +69 -0
- package/src/variant-collapse.ts +1065 -0
- package/src/wire/codex.ts +43 -0
- package/src/wire/coreweave.ts +51 -0
- package/src/wire/gemini-headers.ts +74 -0
- package/src/wire/github-copilot.ts +121 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { type } from "arktype";
|
|
2
|
+
import type { Api, FetchImpl, ModelSpec, Provider } from "../types";
|
|
3
|
+
import { discoveryFetch } from "../utils";
|
|
4
|
+
|
|
5
|
+
const MODELS_PATH = "/models";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Uses a cancellable timer rather than the native abort-timeout helper so
|
|
9
|
+
* successful fast discovery requests do not leave armed timeout signals for
|
|
10
|
+
* concurrent GC to trip over later.
|
|
11
|
+
*/
|
|
12
|
+
async function withOpenAICompatibleDiscoveryTimeout<T>(
|
|
13
|
+
timeoutMs: number,
|
|
14
|
+
run: (signal: AbortSignal) => Promise<T>,
|
|
15
|
+
): Promise<T> {
|
|
16
|
+
const controller = new AbortController();
|
|
17
|
+
const timer = setTimeout(
|
|
18
|
+
() => controller.abort(new DOMException("The operation timed out.", "TimeoutError")),
|
|
19
|
+
timeoutMs,
|
|
20
|
+
);
|
|
21
|
+
try {
|
|
22
|
+
return await run(controller.signal);
|
|
23
|
+
} finally {
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Minimal OpenAI-style model entry shape consumed by discovery.
|
|
30
|
+
*
|
|
31
|
+
* Providers may return additional fields; this type only captures
|
|
32
|
+
* fields that are useful for generic normalization.
|
|
33
|
+
*/
|
|
34
|
+
export interface OpenAICompatibleModelRecord {
|
|
35
|
+
id?: unknown;
|
|
36
|
+
name?: unknown;
|
|
37
|
+
object?: unknown;
|
|
38
|
+
owned_by?: unknown;
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Tolerant envelope for OpenAI-compatible `/models` responses.
|
|
44
|
+
*
|
|
45
|
+
* Common providers return `{ data: [...] }`, but variants such as
|
|
46
|
+
* `{ models: [...] }`, `{ result: [...] }`, or direct arrays are also
|
|
47
|
+
* accepted during extraction.
|
|
48
|
+
*/
|
|
49
|
+
export interface OpenAICompatibleModelsEnvelope {
|
|
50
|
+
data?: unknown;
|
|
51
|
+
models?: unknown;
|
|
52
|
+
result?: unknown;
|
|
53
|
+
items?: unknown;
|
|
54
|
+
[key: string]: unknown;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const openAICompatibleModelRecordSchema = type({
|
|
58
|
+
id: "string >= 1",
|
|
59
|
+
"name?": "string | null",
|
|
60
|
+
"object?": "unknown",
|
|
61
|
+
"owned_by?": "unknown",
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const openAICompatibleModelsEnvelopeSchema = type({
|
|
65
|
+
"data?": "unknown",
|
|
66
|
+
"models?": "unknown",
|
|
67
|
+
"result?": "unknown",
|
|
68
|
+
"items?": "unknown",
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const openAICompatibleModelsPayloadSchema = type("unknown[]").or(openAICompatibleModelsEnvelopeSchema);
|
|
72
|
+
|
|
73
|
+
type ParsedOpenAICompatibleModelRecord = typeof openAICompatibleModelRecordSchema.infer;
|
|
74
|
+
/**
|
|
75
|
+
* Context passed to custom OpenAI-compatible model mappers.
|
|
76
|
+
*/
|
|
77
|
+
export interface OpenAICompatibleModelMapperContext<TApi extends Api> {
|
|
78
|
+
api: TApi;
|
|
79
|
+
provider: Provider;
|
|
80
|
+
baseUrl: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Options for fetching and normalizing OpenAI-compatible `/models` catalogs.
|
|
85
|
+
*/
|
|
86
|
+
export interface FetchOpenAICompatibleModelsOptions<TApi extends Api> {
|
|
87
|
+
/** API type assigned to normalized models. */
|
|
88
|
+
api: TApi;
|
|
89
|
+
/** Provider id assigned to normalized models. */
|
|
90
|
+
provider: Provider;
|
|
91
|
+
/** Provider base URL used for both fetch and normalized model records. */
|
|
92
|
+
baseUrl: string;
|
|
93
|
+
/** Optional bearer token for Authorization header. */
|
|
94
|
+
apiKey?: string;
|
|
95
|
+
/** Additional request headers. */
|
|
96
|
+
headers?: Record<string, string>;
|
|
97
|
+
/** Optional AbortSignal for request cancellation; caller owns its lifecycle. */
|
|
98
|
+
signal?: AbortSignal;
|
|
99
|
+
/** Optional cancellable request timeout used when `signal` is omitted. */
|
|
100
|
+
timeoutMs?: number;
|
|
101
|
+
/** Optional fetch implementation override for testing/custom runtimes. */
|
|
102
|
+
fetch?: FetchImpl;
|
|
103
|
+
/**
|
|
104
|
+
* Optional post-normalization filter.
|
|
105
|
+
* Return false to skip a model.
|
|
106
|
+
*/
|
|
107
|
+
filterModel?: (entry: OpenAICompatibleModelRecord, model: ModelSpec<TApi>) => boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Optional mapper override for provider-specific quirks.
|
|
110
|
+
* Return null to skip a model.
|
|
111
|
+
*/
|
|
112
|
+
mapModel?: (
|
|
113
|
+
entry: OpenAICompatibleModelRecord,
|
|
114
|
+
defaults: ModelSpec<TApi>,
|
|
115
|
+
context: OpenAICompatibleModelMapperContext<TApi>,
|
|
116
|
+
) => ModelSpec<TApi> | null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Fetches and normalizes an OpenAI-compatible `/models` catalog.
|
|
121
|
+
*
|
|
122
|
+
* Returns `null` on transport/protocol failures.
|
|
123
|
+
* Returns `[]` only when the endpoint responds successfully with no usable models.
|
|
124
|
+
*/
|
|
125
|
+
export async function fetchOpenAICompatibleModels<TApi extends Api>(
|
|
126
|
+
options: FetchOpenAICompatibleModelsOptions<TApi>,
|
|
127
|
+
): Promise<ModelSpec<TApi>[] | null> {
|
|
128
|
+
const baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
129
|
+
if (!baseUrl) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const requestHeaders: Record<string, string> = {
|
|
134
|
+
Accept: "application/json",
|
|
135
|
+
...options.headers,
|
|
136
|
+
};
|
|
137
|
+
if (options.apiKey) {
|
|
138
|
+
requestHeaders.Authorization = `Bearer ${options.apiKey}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const fetchImpl = discoveryFetch(options.fetch);
|
|
142
|
+
const fetchPayload = async (signal?: AbortSignal): Promise<unknown | null> => {
|
|
143
|
+
let response: Response;
|
|
144
|
+
try {
|
|
145
|
+
response = await fetchImpl(`${baseUrl}${MODELS_PATH}`, {
|
|
146
|
+
method: "GET",
|
|
147
|
+
headers: requestHeaders,
|
|
148
|
+
signal,
|
|
149
|
+
});
|
|
150
|
+
} catch {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!response.ok) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
return await response.json();
|
|
160
|
+
} catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
const payload =
|
|
165
|
+
options.signal !== undefined
|
|
166
|
+
? await fetchPayload(options.signal)
|
|
167
|
+
: options.timeoutMs !== undefined
|
|
168
|
+
? await withOpenAICompatibleDiscoveryTimeout(options.timeoutMs, fetchPayload)
|
|
169
|
+
: await fetchPayload();
|
|
170
|
+
if (payload === null) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const entries = extractModelEntries(payload);
|
|
175
|
+
if (entries === null) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const context: OpenAICompatibleModelMapperContext<TApi> = {
|
|
180
|
+
api: options.api,
|
|
181
|
+
provider: options.provider,
|
|
182
|
+
baseUrl,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const deduped = new Map<string, ModelSpec<TApi>>();
|
|
186
|
+
for (const entry of entries) {
|
|
187
|
+
const defaults: ModelSpec<TApi> = {
|
|
188
|
+
id: entry.id,
|
|
189
|
+
name: typeof entry.name === "string" && entry.name.length > 0 ? entry.name : entry.id,
|
|
190
|
+
api: options.api,
|
|
191
|
+
provider: options.provider,
|
|
192
|
+
baseUrl,
|
|
193
|
+
reasoning: false,
|
|
194
|
+
input: ["text"],
|
|
195
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
196
|
+
contextWindow: null,
|
|
197
|
+
maxTokens: null,
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// `mapModel` returning null skips the entry (documented contract); only a
|
|
201
|
+
// missing mapper falls back to the defaults.
|
|
202
|
+
const mapped = options.mapModel ? options.mapModel(entry, defaults, context) : defaults;
|
|
203
|
+
if (!mapped || typeof mapped.id !== "string" || mapped.id.length === 0) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (options.filterModel && !options.filterModel(entry, mapped)) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
deduped.set(mapped.id, mapped);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return Array.from(deduped.values()).sort((left, right) => left.id.localeCompare(right.id));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function normalizeBaseUrl(baseUrl: string): string {
|
|
216
|
+
const trimmed = baseUrl.trim();
|
|
217
|
+
if (!trimmed) {
|
|
218
|
+
return "";
|
|
219
|
+
}
|
|
220
|
+
return trimmed.endsWith("/") ? trimmed.slice(0, -1) : trimmed;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function extractModelEntries(payload: unknown): ParsedOpenAICompatibleModelRecord[] | null {
|
|
224
|
+
return extractModelEntriesFromNode(payload);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function extractModelEntriesFromNode(node: unknown): ParsedOpenAICompatibleModelRecord[] | null {
|
|
228
|
+
const parsedPayload = openAICompatibleModelsPayloadSchema(node);
|
|
229
|
+
if (parsedPayload instanceof type.errors) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
if (Array.isArray(parsedPayload)) {
|
|
233
|
+
const parsedEntries = parsedPayload
|
|
234
|
+
.map(entry => openAICompatibleModelRecordSchema(entry))
|
|
235
|
+
.flatMap(entry => (entry instanceof type.errors ? [] : [entry]));
|
|
236
|
+
return parsedEntries;
|
|
237
|
+
}
|
|
238
|
+
for (const candidate of [parsedPayload.data, parsedPayload.models, parsedPayload.result, parsedPayload.items]) {
|
|
239
|
+
if (candidate === undefined) {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
const nested = extractModelEntriesFromNode(candidate);
|
|
243
|
+
if (nested !== null) {
|
|
244
|
+
return nested;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return null;
|
|
249
|
+
}
|
package/src/effort.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** User-facing thinking levels, ordered least to most intensive. */
|
|
2
|
+
export const enum Effort {
|
|
3
|
+
Minimal = "minimal",
|
|
4
|
+
Low = "low",
|
|
5
|
+
Medium = "medium",
|
|
6
|
+
High = "high",
|
|
7
|
+
XHigh = "xhigh",
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const THINKING_EFFORTS: readonly Effort[] = [
|
|
11
|
+
Effort.Minimal,
|
|
12
|
+
Effort.Low,
|
|
13
|
+
Effort.Medium,
|
|
14
|
+
Effort.High,
|
|
15
|
+
Effort.XHigh,
|
|
16
|
+
];
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const FIREWORKS_WIRE_PREFIX = "accounts/fireworks/models/";
|
|
2
|
+
const FIREPASS_WIRE_PREFIX = "accounts/fireworks/routers/";
|
|
3
|
+
const VERSION_SEPARATOR_PATTERN = /(?<=\d)p(?=\d)/g;
|
|
4
|
+
const VERSION_DOT_PATTERN = /(?<=\d)\.(?=\d)/g;
|
|
5
|
+
|
|
6
|
+
export function toFireworksPublicModelId(modelId: string): string {
|
|
7
|
+
const stripped = modelId.startsWith(FIREWORKS_WIRE_PREFIX) ? modelId.slice(FIREWORKS_WIRE_PREFIX.length) : modelId;
|
|
8
|
+
return stripped.replace(VERSION_SEPARATOR_PATTERN, ".");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function toFireworksWireModelId(modelId: string): string {
|
|
12
|
+
const stripped = modelId.startsWith(FIREWORKS_WIRE_PREFIX) ? modelId.slice(FIREWORKS_WIRE_PREFIX.length) : modelId;
|
|
13
|
+
return `${FIREWORKS_WIRE_PREFIX}${stripped.replace(VERSION_DOT_PATTERN, "p")}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Fire Pass exposes its Kimi K2.6 Turbo subscription through a dedicated router
|
|
18
|
+
* endpoint at `accounts/fireworks/routers/<id>` rather than the `models/` namespace.
|
|
19
|
+
* We keep a friendly public id (e.g. `kimi-k2.6-turbo`) in the catalog and translate
|
|
20
|
+
* to the wire form (`accounts/fireworks/routers/kimi-k2p6-turbo`) at request time.
|
|
21
|
+
*/
|
|
22
|
+
export function toFirepassPublicModelId(modelId: string): string {
|
|
23
|
+
const stripped = modelId.startsWith(FIREPASS_WIRE_PREFIX) ? modelId.slice(FIREPASS_WIRE_PREFIX.length) : modelId;
|
|
24
|
+
return stripped.replace(VERSION_SEPARATOR_PATTERN, ".");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function toFirepassWireModelId(modelId: string): string {
|
|
28
|
+
const stripped = modelId.startsWith(FIREPASS_WIRE_PREFIX) ? modelId.slice(FIREPASS_WIRE_PREFIX.length) : modelId;
|
|
29
|
+
return `${FIREPASS_WIRE_PREFIX}${stripped.replace(VERSION_DOT_PATTERN, "p")}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Public-id suffix marking a Fireworks "Fast" serving-path variant. Fast is a
|
|
34
|
+
* higher-throughput route (100+ tok/s) exposed under a dedicated router id
|
|
35
|
+
* (`accounts/fireworks/routers/<id>-fast`), not a separate model — same weights,
|
|
36
|
+
* higher price, no Priority tier. We keep a friendly `<id>-fast` public id and
|
|
37
|
+
* translate it to the router wire form at request time (compat
|
|
38
|
+
* `wireModelIdMode: "firepass"`). See https://docs.fireworks.ai/serverless/serving-paths.
|
|
39
|
+
*/
|
|
40
|
+
export const FIREWORKS_FAST_SUFFIX = "-fast";
|
|
41
|
+
|
|
42
|
+
/** True for a Fireworks public model id that selects the Fast serving path. */
|
|
43
|
+
export function isFireworksFastModelId(modelId: string): boolean {
|
|
44
|
+
return modelId.endsWith(FIREWORKS_FAST_SUFFIX);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Strip the Fast suffix to recover the base (Standard-tier) model id. */
|
|
48
|
+
export function toFireworksBaseModelId(modelId: string): string {
|
|
49
|
+
return modelId.endsWith(FIREWORKS_FAST_SUFFIX) ? modelId.slice(0, -FIREWORKS_FAST_SUFFIX.length) : modelId;
|
|
50
|
+
}
|
package/src/hosts.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Known model-endpoint host classification — the single vocabulary for the
|
|
3
|
+
* `provider === id || baseUrl.includes(marker)` idiom that gates wire-level
|
|
4
|
+
* behavior (compat detection, routing, header shaping, watchdog floors).
|
|
5
|
+
*
|
|
6
|
+
* Markers are case-insensitive substrings matched against the base URL, NOT
|
|
7
|
+
* parsed hostnames: proxies regularly embed the upstream host in a path
|
|
8
|
+
* segment, and the historical call sites all used substring semantics.
|
|
9
|
+
* Callers that need strict hostname matching — where a substring false
|
|
10
|
+
* positive is dangerous, e.g. the Anthropic official-endpoint OAuth gate —
|
|
11
|
+
* parse the URL and compare the hostname themselves.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface HostClassSpec {
|
|
15
|
+
/** Provider ids that imply this host class regardless of baseUrl. */
|
|
16
|
+
readonly providers?: readonly string[];
|
|
17
|
+
/** Provider-id prefixes that imply this host class (e.g. `xiaomi-token-plan-`). */
|
|
18
|
+
readonly providerPrefixes?: readonly string[];
|
|
19
|
+
/** Lowercase ASCII substrings matched case-insensitively against the base URL. */
|
|
20
|
+
readonly urlMarkers: readonly string[];
|
|
21
|
+
// Strict hostname matching is intentionally not modeled here: the one
|
|
22
|
+
// auth-sensitive consumer (Anthropic official-endpoint) parses the URL
|
|
23
|
+
// itself; every other call site is benign and uses substring matching.
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const KNOWN_HOSTS = {
|
|
27
|
+
openai: { providers: ["openai"], urlMarkers: ["api.openai.com"] },
|
|
28
|
+
azureOpenAI: {
|
|
29
|
+
providers: ["azure"],
|
|
30
|
+
urlMarkers: [".openai.azure.com", "azure.com/openai", "models.inference.ai.azure.com"],
|
|
31
|
+
},
|
|
32
|
+
openrouter: { providers: ["openrouter"], urlMarkers: ["openrouter.ai"] },
|
|
33
|
+
vercelAIGateway: { providers: ["vercel-ai-gateway"], urlMarkers: ["ai-gateway.vercel.sh"] },
|
|
34
|
+
githubCopilot: { providers: ["github-copilot"], urlMarkers: ["githubcopilot.com", "copilot-api."] },
|
|
35
|
+
anthropic: { providers: ["anthropic"], urlMarkers: ["api.anthropic.com"] },
|
|
36
|
+
/** DeepSeek's first-party API only — gates direct-API quirks (max_tokens field, thinking extraBody). */
|
|
37
|
+
deepseekDirect: { providers: ["deepseek"], urlMarkers: ["api.deepseek.com"] },
|
|
38
|
+
/** Any DeepSeek-operated host (first-party API, web-chat fronts). Wider than `deepseekDirect` on purpose. */
|
|
39
|
+
deepseekFamily: { providers: ["deepseek"], urlMarkers: ["deepseek.com"] },
|
|
40
|
+
cerebras: { providers: ["cerebras"], urlMarkers: ["cerebras.ai"] },
|
|
41
|
+
zai: { providers: ["zai"], urlMarkers: ["api.z.ai"] },
|
|
42
|
+
zhipu: { providers: ["zhipu-coding-plan"], urlMarkers: ["open.bigmodel.cn"] },
|
|
43
|
+
kilo: { providers: ["kilo"], urlMarkers: ["api.kilo.ai"] },
|
|
44
|
+
alibabaDashscope: { providers: ["alibaba-coding-plan"], urlMarkers: ["dashscope"] },
|
|
45
|
+
umans: { providers: ["umans"], urlMarkers: ["api.code.umans.ai"] },
|
|
46
|
+
xiaomi: { providers: ["xiaomi"], providerPrefixes: ["xiaomi-token-plan-"], urlMarkers: ["xiaomimimo.com"] },
|
|
47
|
+
xai: { providers: ["xai"], urlMarkers: ["api.x.ai"] },
|
|
48
|
+
mistral: { providers: ["mistral"], urlMarkers: ["mistral.ai"] },
|
|
49
|
+
together: { providers: ["together"], urlMarkers: ["api.together.xyz"] },
|
|
50
|
+
/** URL-only on purpose: the `fireworks`/`firepass` providers route per-model and not every model is Fireworks-shaped. */
|
|
51
|
+
fireworks: { urlMarkers: ["fireworks.ai"] },
|
|
52
|
+
groq: { providers: ["groq"], urlMarkers: ["api.groq.com"] },
|
|
53
|
+
minimax: {
|
|
54
|
+
providers: ["minimax", "minimax-code", "minimax-code-cn"],
|
|
55
|
+
urlMarkers: ["api.minimax.io", "api.minimaxi.com"],
|
|
56
|
+
},
|
|
57
|
+
qwenPortal: { providers: ["qwen-portal"], urlMarkers: ["portal.qwen.ai"] },
|
|
58
|
+
/** NVIDIA NIM (`integrate.api.nvidia.com`). Qwen NIM endpoints take `chat_template_kwargs.enable_thinking`, never top-level `enable_thinking`. */
|
|
59
|
+
nvidia: { providers: ["nvidia"], urlMarkers: ["integrate.api.nvidia.com"] },
|
|
60
|
+
moonshotNative: { providers: ["moonshot", "kimi-code"], urlMarkers: ["api.moonshot.ai", "api.kimi.com"] },
|
|
61
|
+
opencode: { providers: ["opencode-go", "opencode-zen"], urlMarkers: ["opencode.ai"] },
|
|
62
|
+
chutes: { urlMarkers: ["chutes.ai"] },
|
|
63
|
+
} as const satisfies Record<string, HostClassSpec>;
|
|
64
|
+
|
|
65
|
+
export type KnownHost = keyof typeof KNOWN_HOSTS;
|
|
66
|
+
|
|
67
|
+
/** URL-only host check (for call sites that have no provider id, e.g. raw env config). */
|
|
68
|
+
export function hostMatchesUrl(baseUrl: string | undefined, host: KnownHost): boolean {
|
|
69
|
+
if (!baseUrl) return false;
|
|
70
|
+
const spec: HostClassSpec = KNOWN_HOSTS[host];
|
|
71
|
+
for (const marker of spec.urlMarkers) {
|
|
72
|
+
if (includesAsciiCaseInsensitive(baseUrl, marker)) return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Provider-or-URL host check — the canonical `provider === id || baseUrl.includes(marker)` idiom. */
|
|
78
|
+
export function modelMatchesHost(model: { provider: string; baseUrl: string }, host: KnownHost): boolean {
|
|
79
|
+
const spec: HostClassSpec = KNOWN_HOSTS[host];
|
|
80
|
+
if (spec.providers) {
|
|
81
|
+
for (const provider of spec.providers) {
|
|
82
|
+
if (model.provider === provider) return true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (spec.providerPrefixes) {
|
|
86
|
+
for (const prefix of spec.providerPrefixes) {
|
|
87
|
+
if (model.provider.startsWith(prefix)) return true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return hostMatchesUrl(model.baseUrl, host);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function includesAsciiCaseInsensitive(value: string, lowerNeedle: string): boolean {
|
|
94
|
+
const needleLength = lowerNeedle.length;
|
|
95
|
+
const end = value.length - needleLength;
|
|
96
|
+
for (let start = 0; start <= end; start++) {
|
|
97
|
+
let offset = 0;
|
|
98
|
+
for (; offset < needleLength; offset++) {
|
|
99
|
+
if ((value.charCodeAt(start + offset) | 0x20) !== lowerNeedle.charCodeAt(offset)) break;
|
|
100
|
+
}
|
|
101
|
+
if (offset === needleLength) return true;
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// --- Endpoint-shape predicates (URL path/verb shapes, not vendor hosts) ---
|
|
107
|
+
|
|
108
|
+
/** Vertex AI express-mode OpenAI-compatible endpoint (`…/endpoints/openapi`). */
|
|
109
|
+
export function isVertexExpressOpenAIUrl(baseUrl: string): boolean {
|
|
110
|
+
return baseUrl.includes("/endpoints/openapi");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Vertex AI Anthropic raw-predict endpoints (`:streamRawPredict` / `:rawPredict`). */
|
|
114
|
+
export function isVertexRawPredictUrl(baseUrl: string): boolean {
|
|
115
|
+
return baseUrl.includes(":streamRawPredict") || baseUrl.includes(":rawPredict");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Azure OpenAI deployment-scoped path (`…/deployments/<name>/…`). */
|
|
119
|
+
export function isAzureDeploymentsUrl(baseUrl: string): boolean {
|
|
120
|
+
return baseUrl.includes("/deployments/");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Alibaba DashScope consumer `compatible-mode` endpoint (rejects multimodal arrays for some text-only SKUs). */
|
|
124
|
+
export function isDashscopeCompatibleModeUrl(baseUrl: string): boolean {
|
|
125
|
+
const normalized = baseUrl.toLowerCase();
|
|
126
|
+
return (
|
|
127
|
+
normalized.includes("dashscope") && normalized.includes("aliyuncs.com") && normalized.includes("/compatible-mode")
|
|
128
|
+
);
|
|
129
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memoized proxy-reference index over the bundled model catalog.
|
|
3
|
+
*
|
|
4
|
+
* Lazy: walking every bundled model (~12K) triggers thinking enrichment, so the
|
|
5
|
+
* walk is deferred off module load and performed once. Consumers that need
|
|
6
|
+
* non-bundled reference data use the pure builder directly
|
|
7
|
+
* ({@link buildModelReferenceIndex}).
|
|
8
|
+
*/
|
|
9
|
+
import { getBundledModels, getBundledProviders } from "../models";
|
|
10
|
+
import type { Api, Model } from "../types";
|
|
11
|
+
import { buildModelReferenceIndex, type ModelReferenceIndex } from "./reference";
|
|
12
|
+
|
|
13
|
+
let bundledModels: readonly Model<Api>[] | undefined;
|
|
14
|
+
|
|
15
|
+
function getBundledModelList(): readonly Model<Api>[] {
|
|
16
|
+
bundledModels ??= getBundledProviders().flatMap(
|
|
17
|
+
provider => getBundledModels(provider as Parameters<typeof getBundledModels>[0]) as Model<Api>[],
|
|
18
|
+
);
|
|
19
|
+
return bundledModels;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let referenceIndex: ModelReferenceIndex | undefined;
|
|
23
|
+
|
|
24
|
+
/** Proxy-reference index over the bundled catalog. */
|
|
25
|
+
export function getBundledModelReferenceIndex(): ModelReferenceIndex {
|
|
26
|
+
referenceIndex ??= buildModelReferenceIndex(getBundledModelList());
|
|
27
|
+
return referenceIndex;
|
|
28
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-id classification: parse a model id into its family (gemini / anthropic /
|
|
3
|
+
* openai), kind/variant, and version. This is the shared layer both catalog
|
|
4
|
+
* policy rules (`model-thinking.ts`) and downstream consumers build on —
|
|
5
|
+
* classification lives here, the rules that consume it stay with their domain.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type SemVer = {
|
|
9
|
+
major: number;
|
|
10
|
+
minor: number;
|
|
11
|
+
patch: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type GeminiKind = "pro" | "flash";
|
|
15
|
+
export type AnthropicKind = "opus" | "sonnet" | "fable" | "mythos";
|
|
16
|
+
export type OpenAIVariant = "base" | "codex" | "codex-max" | "codex-mini" | "codex-spark" | "mini" | "max" | "nano";
|
|
17
|
+
export type GlmVariant = "base" | "air" | "turbo" | "flash" | "flashx" | "preview";
|
|
18
|
+
|
|
19
|
+
export interface GeminiModel {
|
|
20
|
+
family: "gemini";
|
|
21
|
+
kind: GeminiKind;
|
|
22
|
+
version: SemVer;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AnthropicModel {
|
|
26
|
+
family: "anthropic";
|
|
27
|
+
kind: AnthropicKind;
|
|
28
|
+
version: SemVer;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface OpenAIModel {
|
|
32
|
+
family: "openai";
|
|
33
|
+
variant: OpenAIVariant;
|
|
34
|
+
version: SemVer;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface GlmModel {
|
|
38
|
+
family: "glm";
|
|
39
|
+
/** Suffix variant (`-air`, `-turbo`, `-flash`, `-flashx`, `-preview`); `base` when none. */
|
|
40
|
+
variant: GlmVariant;
|
|
41
|
+
/** Vision SKU — the `v` that attaches directly to the version (`glm-4v`, `glm-4.5v`). */
|
|
42
|
+
vision: boolean;
|
|
43
|
+
version: SemVer;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface UnknownModel {
|
|
47
|
+
family: "unknown";
|
|
48
|
+
id: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type ParsedModel = GeminiModel | AnthropicModel | OpenAIModel | UnknownModel;
|
|
52
|
+
|
|
53
|
+
/** Strip a provider namespace prefix (`openai/gpt-5.4` → `gpt-5.4`). */
|
|
54
|
+
// Cache keyed by model id (a bounded set of bundled/aggregator ids), so no eviction is needed.
|
|
55
|
+
const bareModelIdCache = new Map<string, string>();
|
|
56
|
+
export function bareModelId(modelId: string): string {
|
|
57
|
+
const cached = bareModelIdCache.get(modelId);
|
|
58
|
+
if (cached !== undefined) return cached;
|
|
59
|
+
const p = modelId.lastIndexOf("/");
|
|
60
|
+
const result = p !== -1 ? modelId.slice(p + 1) : modelId;
|
|
61
|
+
bareModelIdCache.set(modelId, result);
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function parseKnownModel(modelId: string): ParsedModel {
|
|
66
|
+
const canonicalId = bareModelId(modelId);
|
|
67
|
+
return (
|
|
68
|
+
parseGeminiModel(canonicalId) ??
|
|
69
|
+
parseAnthropicModel(canonicalId) ??
|
|
70
|
+
parseOpenAIModel(canonicalId) ?? { family: "unknown", id: canonicalId }
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Wrap a parse function in a per-id memo cache. Caches the `null` result too, so
|
|
76
|
+
* repeated misses (the common case — ids of other families) stay O(1) and never
|
|
77
|
+
* re-run the regex/semver work.
|
|
78
|
+
*/
|
|
79
|
+
function parser<T>(parse: (modelId: string) => T | null): (modelId: string) => T | null {
|
|
80
|
+
const cache = new Map<string, T | null>();
|
|
81
|
+
return modelId => {
|
|
82
|
+
const hit = cache.get(modelId);
|
|
83
|
+
if (hit !== undefined || cache.has(modelId)) {
|
|
84
|
+
return hit ?? null;
|
|
85
|
+
}
|
|
86
|
+
const result = parse(modelId);
|
|
87
|
+
cache.set(modelId, result);
|
|
88
|
+
return result;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const GEMINI_SUFFIX = "-preview";
|
|
93
|
+
export const parseGeminiModel = parser((modelId): GeminiModel | null => {
|
|
94
|
+
if (modelId.endsWith(GEMINI_SUFFIX)) {
|
|
95
|
+
modelId = modelId.slice(0, -GEMINI_SUFFIX.length);
|
|
96
|
+
}
|
|
97
|
+
const match = /gemini-(\d+(?:\.\d+){0,2})-(pro|flash)\b/.exec(modelId);
|
|
98
|
+
if (!match) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const version = parseSemVer(match[1]);
|
|
102
|
+
if (!version) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
return { family: "gemini", kind: match[2] as GeminiKind, version };
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
export const parseAnthropicModel = parser((modelId): AnthropicModel | null => {
|
|
109
|
+
const match = /claude-(opus|sonnet|fable|mythos)-(\d{1,2}(?:[.-]\d{1,2}){0,2})\b/.exec(modelId);
|
|
110
|
+
if (!match) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const version = parseSemVer(match[2]);
|
|
114
|
+
if (!version) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
return { family: "anthropic", kind: match[1] as AnthropicKind, version };
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
export const parseOpenAIModel = parser((modelId): OpenAIModel | null => {
|
|
121
|
+
const match = /gpt-(\d+(?:\.\d+){0,2})(?:-(codex-spark|codex-mini|codex-max|codex|mini|max|nano))?\b/.exec(modelId);
|
|
122
|
+
if (!match) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const version = parseSemVer(match[1]);
|
|
126
|
+
if (!version) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
return { family: "openai", variant: (match[2] as OpenAIVariant | undefined) ?? "base", version };
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Parse a GLM (Zhipu / Z.AI) model id into family + variant + vision + version.
|
|
134
|
+
* Shape: `glm-<version>[v][-<variant>]` — e.g. `glm-4.5`, `glm-4.5-air`,
|
|
135
|
+
* `glm-5-turbo`, `glm-4.5v`, `glm-5-preview`. The `v` (vision) attaches to the
|
|
136
|
+
* version; other variants are `-` suffixes. Standalone like `parseAnthropicModel`
|
|
137
|
+
* is used in family.ts — GLM needs no global thinking policy, so it stays out of
|
|
138
|
+
* `parseKnownModel`.
|
|
139
|
+
*/
|
|
140
|
+
export const parseGlmModel = parser((modelId): GlmModel | null => {
|
|
141
|
+
const match = /glm-(\d{1,2}(?:\.\d+)?)(v)?(?:-(air|turbo|flashx|flash|preview))?\b/.exec(modelId);
|
|
142
|
+
if (!match) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const version = parseSemVer(match[1]);
|
|
146
|
+
if (!version) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
family: "glm",
|
|
151
|
+
variant: (match[3] as GlmVariant | undefined) ?? "base",
|
|
152
|
+
vision: match[2] === "v",
|
|
153
|
+
version,
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
export function isFableOrMythos(kind: AnthropicKind): boolean {
|
|
158
|
+
return kind === "fable" || kind === "mythos";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Returns true if the parsed Anthropic model is part of the adaptive-thinking
|
|
163
|
+
* Claude generation at or above a specific capability threshold.
|
|
164
|
+
* - Opus has a configurable minimum version floor (e.g. "4.6", "4.7", "4.8").
|
|
165
|
+
* - Sonnet, Fable, and Mythos all require version 5 or higher.
|
|
166
|
+
*/
|
|
167
|
+
export function isAnthropicAdaptiveGenAtLeast(parsed: AnthropicModel, opusMin: "4.6" | "4.7" | "4.8"): boolean {
|
|
168
|
+
if (parsed.kind === "opus") {
|
|
169
|
+
return semverGte(parsed.version, opusMin);
|
|
170
|
+
}
|
|
171
|
+
// Sonnet 5+, Fable 5+, Mythos 5+, and any future gen-5+ models
|
|
172
|
+
return semverGte(parsed.version, "5");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function createSemVer(major: number, minor: number, patch = 0): SemVer {
|
|
176
|
+
return { major, minor, patch };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// extend this table if we need anything more than 9.10
|
|
180
|
+
const precomputeTable: Record<string, SemVer> = {};
|
|
181
|
+
for (let major = 0; major <= 9; major++) {
|
|
182
|
+
for (let minor = 0; minor <= 10; minor++) {
|
|
183
|
+
const version = createSemVer(major, minor, 0);
|
|
184
|
+
precomputeTable[`${major}.${minor}`] = version;
|
|
185
|
+
precomputeTable[`${major}-${minor}`] = version;
|
|
186
|
+
}
|
|
187
|
+
precomputeTable[`${major}`] = createSemVer(major, 0, 0);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function parseSemVer(version: string): SemVer | null {
|
|
191
|
+
return precomputeTable[version] ?? null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function semverGte(left: SemVer | string, right: SemVer | string): boolean {
|
|
195
|
+
return compareSemVer(left, right) >= 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function semverEqual(left: SemVer | string, right: SemVer | string): boolean {
|
|
199
|
+
return compareSemVer(left, right) === 0;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function compareSemVer(left: SemVer | string | null, right: SemVer | string | null): number {
|
|
203
|
+
left = typeof left === "string" ? parseSemVer(left) : left;
|
|
204
|
+
right = typeof right === "string" ? parseSemVer(right) : right;
|
|
205
|
+
if (!left || !right) return (left ? 1 : 0) - (right ? 1 : 0);
|
|
206
|
+
|
|
207
|
+
if (left.major !== right.major) {
|
|
208
|
+
return left.major - right.major;
|
|
209
|
+
}
|
|
210
|
+
if (left.minor !== right.minor) {
|
|
211
|
+
return left.minor - right.minor;
|
|
212
|
+
}
|
|
213
|
+
return left.patch - right.patch;
|
|
214
|
+
}
|