@theokit/sdk 4.12.2 → 4.13.0
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 +6 -0
- package/dist/cron.cjs +164 -78
- package/dist/cron.cjs.map +1 -1
- package/dist/cron.js +164 -78
- package/dist/cron.js.map +1 -1
- package/dist/eval.cjs +164 -78
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.js +164 -78
- package/dist/eval.js.map +1 -1
- package/dist/index.cjs +178 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +178 -92
- package/dist/index.js.map +1 -1
- package/dist/internal/providers/catalog-loader.d.ts +8 -0
- package/dist/internal/providers/catalog-schema.d.ts +53 -0
- package/dist/internal/providers/catalog-source-models-dev.d.ts +33 -0
- package/dist/models.cjs +332 -257
- package/dist/models.cjs.map +1 -1
- package/dist/models.d.cts +2 -0
- package/dist/models.d.ts +2 -0
- package/dist/models.js +330 -258
- package/dist/models.js.map +1 -1
- package/dist/provider-catalog.json +571 -5
- package/package.json +1 -1
package/dist/models.cjs
CHANGED
|
@@ -1,5 +1,119 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var url = require('url');
|
|
6
|
+
var zod = require('zod');
|
|
7
|
+
var crypto = require('crypto');
|
|
8
|
+
var os = require('os');
|
|
9
|
+
|
|
10
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
11
|
+
// src/internal/providers/catalog-loader.ts
|
|
12
|
+
var MODALITIES = ["text", "audio", "image", "video", "pdf"];
|
|
13
|
+
var costSchema = zod.z.object({
|
|
14
|
+
/** USD per 1M tokens (models.dev convention). */
|
|
15
|
+
input: zod.z.number().nonnegative(),
|
|
16
|
+
output: zod.z.number().nonnegative(),
|
|
17
|
+
cache_read: zod.z.number().nonnegative().optional(),
|
|
18
|
+
cache_write: zod.z.number().nonnegative().optional()
|
|
19
|
+
}).loose();
|
|
20
|
+
var limitSchema = zod.z.object({
|
|
21
|
+
context: zod.z.number().positive(),
|
|
22
|
+
input: zod.z.number().positive().optional(),
|
|
23
|
+
output: zod.z.number().positive().optional()
|
|
24
|
+
}).loose();
|
|
25
|
+
var modalitiesSchema = zod.z.object({
|
|
26
|
+
input: zod.z.array(zod.z.enum(MODALITIES)).optional(),
|
|
27
|
+
output: zod.z.array(zod.z.enum(MODALITIES)).optional()
|
|
28
|
+
}).loose();
|
|
29
|
+
var catalogModelSchema = zod.z.object({
|
|
30
|
+
name: zod.z.string().optional(),
|
|
31
|
+
release_date: zod.z.string().optional(),
|
|
32
|
+
attachment: zod.z.boolean().optional(),
|
|
33
|
+
reasoning: zod.z.boolean().optional(),
|
|
34
|
+
temperature: zod.z.boolean().optional(),
|
|
35
|
+
tool_call: zod.z.boolean().optional(),
|
|
36
|
+
/** theokit extension — maps to ModelCapabilities.supportsStructuredOutput. */
|
|
37
|
+
structured_output: zod.z.boolean().optional(),
|
|
38
|
+
/** theokit extension — maps to ModelCapabilities.supportsCacheControl. */
|
|
39
|
+
cache_control: zod.z.boolean().optional(),
|
|
40
|
+
cost: costSchema.optional(),
|
|
41
|
+
limit: limitSchema.optional(),
|
|
42
|
+
modalities: modalitiesSchema.optional(),
|
|
43
|
+
status: zod.z.enum(["alpha", "beta", "deprecated"]).optional()
|
|
44
|
+
}).loose();
|
|
45
|
+
|
|
46
|
+
// src/internal/providers/registry.ts
|
|
47
|
+
var REGISTRY = /* @__PURE__ */ new Map();
|
|
48
|
+
var ALIASES = /* @__PURE__ */ new Map();
|
|
49
|
+
function getProviderProfile(name) {
|
|
50
|
+
const canonical = ALIASES.get(name) ?? name;
|
|
51
|
+
return REGISTRY.get(canonical);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/internal/providers/catalog-loader.ts
|
|
55
|
+
var __dirname_resolved = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('models.cjs', document.baseURI).href))));
|
|
56
|
+
var modelInfoIndex = /* @__PURE__ */ new Map();
|
|
57
|
+
function getCatalogModelInfo(key) {
|
|
58
|
+
ensureModelIndexLoaded();
|
|
59
|
+
return modelInfoIndex.get(key);
|
|
60
|
+
}
|
|
61
|
+
function patchModelInfo(key, model) {
|
|
62
|
+
ensureModelIndexLoaded();
|
|
63
|
+
modelInfoIndex.set(key, model);
|
|
64
|
+
}
|
|
65
|
+
var _modelIndexLoaded = false;
|
|
66
|
+
function ensureModelIndexLoaded() {
|
|
67
|
+
if (_modelIndexLoaded) return;
|
|
68
|
+
_modelIndexLoaded = true;
|
|
69
|
+
const catalog = loadProviderCatalog();
|
|
70
|
+
for (const entry of Object.values(catalog)) {
|
|
71
|
+
indexEntryModels(entry);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function indexEntryModels(entry) {
|
|
75
|
+
if (entry.models === void 0 || typeof entry.models !== "object") return;
|
|
76
|
+
for (const [modelId, raw] of Object.entries(entry.models)) {
|
|
77
|
+
const parsed = catalogModelSchema.safeParse(raw);
|
|
78
|
+
if (!parsed.success) {
|
|
79
|
+
process.stderr.write(
|
|
80
|
+
`[theokit-sdk] WARN: Skipping malformed catalog model "${entry.id}/${modelId}": ${parsed.error.issues[0]?.message ?? "invalid"}
|
|
81
|
+
`
|
|
82
|
+
);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
modelInfoIndex.set(`${entry.id}/${modelId}`, parsed.data);
|
|
86
|
+
for (const alias of entry.aliases ?? []) {
|
|
87
|
+
const key = `${alias}/${modelId}`;
|
|
88
|
+
if (!modelInfoIndex.has(key)) modelInfoIndex.set(key, parsed.data);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function validateEntry(raw) {
|
|
93
|
+
if (typeof raw.id !== "string" || typeof raw.displayName !== "string" || typeof raw.apiMode !== "string" || typeof raw.authType !== "string" || typeof raw.baseUrl !== "string" || !Array.isArray(raw.envVars) || !Array.isArray(raw.fallbackModels) || raw.capabilities == null || typeof raw.capabilities !== "object") {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
return raw;
|
|
97
|
+
}
|
|
98
|
+
function loadProviderCatalog(opts) {
|
|
99
|
+
const catalogPath = path.join(__dirname_resolved, "provider-catalog.json");
|
|
100
|
+
const rawText = fs.readFileSync(catalogPath, "utf-8");
|
|
101
|
+
let entries = JSON.parse(rawText);
|
|
102
|
+
const result = {};
|
|
103
|
+
for (const raw of entries) {
|
|
104
|
+
const validated = validateEntry(raw);
|
|
105
|
+
if (validated === null) {
|
|
106
|
+
process.stderr.write(
|
|
107
|
+
`[theokit-sdk] WARN: Skipping malformed catalog entry: ${JSON.stringify(raw).slice(0, 100)}
|
|
108
|
+
`
|
|
109
|
+
);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
result[validated.id] = validated;
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
|
|
3
117
|
// src/internal/llm/model-capabilities.ts
|
|
4
118
|
var CONSERVATIVE_DEFAULTS = {
|
|
5
119
|
supportsVision: false,
|
|
@@ -9,268 +123,25 @@ var CONSERVATIVE_DEFAULTS = {
|
|
|
9
123
|
maxContextTokens: 4096,
|
|
10
124
|
maxOutputTokens: 4096
|
|
11
125
|
};
|
|
12
|
-
var EXACT = /* @__PURE__ */ new Map([
|
|
13
|
-
// OpenAI family
|
|
14
|
-
[
|
|
15
|
-
"openai/gpt-4o",
|
|
16
|
-
{
|
|
17
|
-
supportsVision: true,
|
|
18
|
-
supportsStructuredOutput: true,
|
|
19
|
-
supportsToolUse: true,
|
|
20
|
-
supportsCacheControl: false,
|
|
21
|
-
maxContextTokens: 128e3,
|
|
22
|
-
maxOutputTokens: 16384
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
[
|
|
26
|
-
"openai/gpt-4o-mini",
|
|
27
|
-
{
|
|
28
|
-
supportsVision: true,
|
|
29
|
-
supportsStructuredOutput: true,
|
|
30
|
-
supportsToolUse: true,
|
|
31
|
-
supportsCacheControl: false,
|
|
32
|
-
maxContextTokens: 128e3,
|
|
33
|
-
maxOutputTokens: 16384
|
|
34
|
-
}
|
|
35
|
-
],
|
|
36
|
-
[
|
|
37
|
-
"openai/gpt-4-turbo",
|
|
38
|
-
{
|
|
39
|
-
supportsVision: true,
|
|
40
|
-
supportsStructuredOutput: false,
|
|
41
|
-
supportsToolUse: true,
|
|
42
|
-
supportsCacheControl: false,
|
|
43
|
-
maxContextTokens: 128e3,
|
|
44
|
-
maxOutputTokens: 4096
|
|
45
|
-
}
|
|
46
|
-
],
|
|
47
|
-
[
|
|
48
|
-
"openai/o1",
|
|
49
|
-
{
|
|
50
|
-
supportsVision: false,
|
|
51
|
-
supportsStructuredOutput: true,
|
|
52
|
-
supportsToolUse: true,
|
|
53
|
-
supportsCacheControl: false,
|
|
54
|
-
maxContextTokens: 2e5,
|
|
55
|
-
maxOutputTokens: 1e5
|
|
56
|
-
}
|
|
57
|
-
],
|
|
58
|
-
[
|
|
59
|
-
"openai/o3",
|
|
60
|
-
{
|
|
61
|
-
supportsVision: false,
|
|
62
|
-
supportsStructuredOutput: true,
|
|
63
|
-
supportsToolUse: true,
|
|
64
|
-
supportsCacheControl: false,
|
|
65
|
-
maxContextTokens: 2e5,
|
|
66
|
-
maxOutputTokens: 1e5
|
|
67
|
-
}
|
|
68
|
-
],
|
|
69
|
-
[
|
|
70
|
-
// GPT-4.1 — 1M-context flagship; multimodal + structured output (RADAR #92.a).
|
|
71
|
-
"openai/gpt-4.1",
|
|
72
|
-
{
|
|
73
|
-
supportsVision: true,
|
|
74
|
-
supportsStructuredOutput: true,
|
|
75
|
-
supportsToolUse: true,
|
|
76
|
-
supportsCacheControl: false,
|
|
77
|
-
maxContextTokens: 1047576,
|
|
78
|
-
maxOutputTokens: 32768
|
|
79
|
-
}
|
|
80
|
-
],
|
|
81
|
-
// Anthropic family
|
|
82
|
-
[
|
|
83
|
-
"anthropic/claude-opus-4",
|
|
84
|
-
{
|
|
85
|
-
supportsVision: true,
|
|
86
|
-
supportsStructuredOutput: false,
|
|
87
|
-
supportsToolUse: true,
|
|
88
|
-
supportsCacheControl: true,
|
|
89
|
-
maxContextTokens: 2e5,
|
|
90
|
-
maxOutputTokens: 32e3
|
|
91
|
-
}
|
|
92
|
-
],
|
|
93
|
-
[
|
|
94
|
-
"anthropic/claude-sonnet-4",
|
|
95
|
-
{
|
|
96
|
-
supportsVision: true,
|
|
97
|
-
supportsStructuredOutput: false,
|
|
98
|
-
supportsToolUse: true,
|
|
99
|
-
supportsCacheControl: true,
|
|
100
|
-
maxContextTokens: 2e5,
|
|
101
|
-
maxOutputTokens: 16e3
|
|
102
|
-
}
|
|
103
|
-
],
|
|
104
|
-
[
|
|
105
|
-
"anthropic/claude-3-5-sonnet",
|
|
106
|
-
{
|
|
107
|
-
supportsVision: true,
|
|
108
|
-
supportsStructuredOutput: false,
|
|
109
|
-
supportsToolUse: true,
|
|
110
|
-
supportsCacheControl: true,
|
|
111
|
-
maxContextTokens: 2e5,
|
|
112
|
-
maxOutputTokens: 8192
|
|
113
|
-
}
|
|
114
|
-
],
|
|
115
|
-
[
|
|
116
|
-
"anthropic/claude-3-5-sonnet-latest",
|
|
117
|
-
{
|
|
118
|
-
supportsVision: true,
|
|
119
|
-
supportsStructuredOutput: false,
|
|
120
|
-
supportsToolUse: true,
|
|
121
|
-
supportsCacheControl: true,
|
|
122
|
-
maxContextTokens: 2e5,
|
|
123
|
-
maxOutputTokens: 8192
|
|
124
|
-
}
|
|
125
|
-
],
|
|
126
|
-
[
|
|
127
|
-
"anthropic/claude-3-5-haiku-latest",
|
|
128
|
-
{
|
|
129
|
-
supportsVision: false,
|
|
130
|
-
supportsStructuredOutput: false,
|
|
131
|
-
supportsToolUse: true,
|
|
132
|
-
supportsCacheControl: true,
|
|
133
|
-
maxContextTokens: 2e5,
|
|
134
|
-
maxOutputTokens: 8192
|
|
135
|
-
}
|
|
136
|
-
],
|
|
137
|
-
[
|
|
138
|
-
"anthropic/claude-3-haiku",
|
|
139
|
-
{
|
|
140
|
-
supportsVision: true,
|
|
141
|
-
supportsStructuredOutput: false,
|
|
142
|
-
supportsToolUse: true,
|
|
143
|
-
supportsCacheControl: true,
|
|
144
|
-
maxContextTokens: 2e5,
|
|
145
|
-
maxOutputTokens: 4096
|
|
146
|
-
}
|
|
147
|
-
],
|
|
148
|
-
[
|
|
149
|
-
"anthropic/claude-3-opus",
|
|
150
|
-
{
|
|
151
|
-
supportsVision: true,
|
|
152
|
-
supportsStructuredOutput: false,
|
|
153
|
-
supportsToolUse: true,
|
|
154
|
-
supportsCacheControl: true,
|
|
155
|
-
maxContextTokens: 2e5,
|
|
156
|
-
maxOutputTokens: 4096
|
|
157
|
-
}
|
|
158
|
-
],
|
|
159
|
-
// Dot-form OpenRouter slugs theocode uses (RADAR #92.a). These are the same
|
|
160
|
-
// models as their dash-form siblings above; capability parity is intentional.
|
|
161
|
-
// Without these entries the dotted slugs fall through to the 4096 default
|
|
162
|
-
// (`anthropic/claude-3.5-sonnet` ≠ `anthropic/claude-3-5-sonnet`).
|
|
163
|
-
[
|
|
164
|
-
"anthropic/claude-opus-4.1",
|
|
165
|
-
{
|
|
166
|
-
supportsVision: true,
|
|
167
|
-
supportsStructuredOutput: false,
|
|
168
|
-
supportsToolUse: true,
|
|
169
|
-
supportsCacheControl: true,
|
|
170
|
-
maxContextTokens: 2e5,
|
|
171
|
-
maxOutputTokens: 32e3
|
|
172
|
-
}
|
|
173
|
-
],
|
|
174
|
-
[
|
|
175
|
-
"anthropic/claude-sonnet-4.5",
|
|
176
|
-
{
|
|
177
|
-
supportsVision: true,
|
|
178
|
-
supportsStructuredOutput: false,
|
|
179
|
-
supportsToolUse: true,
|
|
180
|
-
supportsCacheControl: true,
|
|
181
|
-
maxContextTokens: 2e5,
|
|
182
|
-
maxOutputTokens: 16e3
|
|
183
|
-
}
|
|
184
|
-
],
|
|
185
|
-
[
|
|
186
|
-
"anthropic/claude-3.5-sonnet",
|
|
187
|
-
{
|
|
188
|
-
supportsVision: true,
|
|
189
|
-
supportsStructuredOutput: false,
|
|
190
|
-
supportsToolUse: true,
|
|
191
|
-
supportsCacheControl: true,
|
|
192
|
-
maxContextTokens: 2e5,
|
|
193
|
-
maxOutputTokens: 8192
|
|
194
|
-
}
|
|
195
|
-
],
|
|
196
|
-
// Cheap OpenRouter slugs (RADAR #92.a) — previously fell to the 4096
|
|
197
|
-
// CONSERVATIVE default. toolUse on; vision/structuredOutput only for Gemini.
|
|
198
|
-
[
|
|
199
|
-
"qwen/qwen3-coder-30b-a3b-instruct",
|
|
200
|
-
{
|
|
201
|
-
supportsVision: false,
|
|
202
|
-
supportsStructuredOutput: false,
|
|
203
|
-
supportsToolUse: true,
|
|
204
|
-
supportsCacheControl: false,
|
|
205
|
-
maxContextTokens: 16e4,
|
|
206
|
-
maxOutputTokens: 8e3
|
|
207
|
-
}
|
|
208
|
-
],
|
|
209
|
-
[
|
|
210
|
-
"deepseek/deepseek-v4-flash",
|
|
211
|
-
{
|
|
212
|
-
supportsVision: false,
|
|
213
|
-
supportsStructuredOutput: false,
|
|
214
|
-
supportsToolUse: true,
|
|
215
|
-
supportsCacheControl: false,
|
|
216
|
-
maxContextTokens: 1048576,
|
|
217
|
-
maxOutputTokens: 8e3
|
|
218
|
-
}
|
|
219
|
-
],
|
|
220
|
-
[
|
|
221
|
-
"deepseek/deepseek-v3.2",
|
|
222
|
-
{
|
|
223
|
-
supportsVision: false,
|
|
224
|
-
supportsStructuredOutput: false,
|
|
225
|
-
supportsToolUse: true,
|
|
226
|
-
supportsCacheControl: false,
|
|
227
|
-
maxContextTokens: 131072,
|
|
228
|
-
maxOutputTokens: 8e3
|
|
229
|
-
}
|
|
230
|
-
],
|
|
231
|
-
[
|
|
232
|
-
"z-ai/glm-4.7-flash",
|
|
233
|
-
{
|
|
234
|
-
supportsVision: false,
|
|
235
|
-
supportsStructuredOutput: false,
|
|
236
|
-
supportsToolUse: true,
|
|
237
|
-
supportsCacheControl: false,
|
|
238
|
-
maxContextTokens: 202752,
|
|
239
|
-
maxOutputTokens: 8e3
|
|
240
|
-
}
|
|
241
|
-
],
|
|
242
|
-
[
|
|
243
|
-
"google/gemini-2.5-flash-lite",
|
|
244
|
-
{
|
|
245
|
-
supportsVision: true,
|
|
246
|
-
supportsStructuredOutput: true,
|
|
247
|
-
supportsToolUse: true,
|
|
248
|
-
supportsCacheControl: false,
|
|
249
|
-
maxContextTokens: 1048576,
|
|
250
|
-
maxOutputTokens: 8e3
|
|
251
|
-
}
|
|
252
|
-
],
|
|
253
|
-
[
|
|
254
|
-
"google/gemini-2.5-pro",
|
|
255
|
-
{
|
|
256
|
-
supportsVision: true,
|
|
257
|
-
supportsStructuredOutput: true,
|
|
258
|
-
supportsToolUse: true,
|
|
259
|
-
supportsCacheControl: false,
|
|
260
|
-
maxContextTokens: 1048576,
|
|
261
|
-
maxOutputTokens: 8e3
|
|
262
|
-
}
|
|
263
|
-
]
|
|
264
|
-
]);
|
|
265
126
|
var ROUTING_PREFIXES = ["openrouter/", "vertex/", "bedrock/"];
|
|
127
|
+
function capsFromCatalog(m) {
|
|
128
|
+
return {
|
|
129
|
+
supportsVision: m.modalities?.input?.includes("image") ?? m.attachment ?? false,
|
|
130
|
+
supportsStructuredOutput: m.structured_output ?? false,
|
|
131
|
+
supportsToolUse: m.tool_call ?? false,
|
|
132
|
+
supportsCacheControl: m.cache_control ?? false,
|
|
133
|
+
maxContextTokens: m.limit?.context ?? CONSERVATIVE_DEFAULTS.maxContextTokens,
|
|
134
|
+
maxOutputTokens: m.limit?.output ?? CONSERVATIVE_DEFAULTS.maxOutputTokens
|
|
135
|
+
};
|
|
136
|
+
}
|
|
266
137
|
function resolveModelCapabilities(modelId) {
|
|
267
138
|
const bare = stripVariantSuffix(stripRoutingPrefix(modelId));
|
|
268
|
-
const
|
|
269
|
-
if (
|
|
139
|
+
const fromIndex = getCatalogModelInfo(bare);
|
|
140
|
+
if (fromIndex !== void 0) return capsFromCatalog(fromIndex);
|
|
270
141
|
const withVendor = inferVendorPrefix(bare);
|
|
271
142
|
if (withVendor !== bare) {
|
|
272
|
-
const vendored =
|
|
273
|
-
if (vendored !== void 0) return vendored;
|
|
143
|
+
const vendored = getCatalogModelInfo(withVendor);
|
|
144
|
+
if (vendored !== void 0) return capsFromCatalog(vendored);
|
|
274
145
|
}
|
|
275
146
|
return CONSERVATIVE_DEFAULTS;
|
|
276
147
|
}
|
|
@@ -342,8 +213,212 @@ function toModelOption(modelId) {
|
|
|
342
213
|
};
|
|
343
214
|
}
|
|
344
215
|
|
|
216
|
+
// src/errors.ts
|
|
217
|
+
var TheokitAgentError = class extends Error {
|
|
218
|
+
name = "TheokitAgentError";
|
|
219
|
+
isRetryable;
|
|
220
|
+
code;
|
|
221
|
+
protoErrorCode;
|
|
222
|
+
metadata;
|
|
223
|
+
constructor(message, options = {}) {
|
|
224
|
+
super(message, options.cause !== void 0 ? { cause: options.cause } : void 0);
|
|
225
|
+
this.isRetryable = options.isRetryable ?? false;
|
|
226
|
+
if (options.code !== void 0) this.code = options.code;
|
|
227
|
+
if (options.protoErrorCode !== void 0) this.protoErrorCode = options.protoErrorCode;
|
|
228
|
+
if (options.metadata !== void 0) this.metadata = options.metadata;
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
var ConfigurationError = class extends TheokitAgentError {
|
|
232
|
+
name = "ConfigurationError";
|
|
233
|
+
constructor(message, options = {}) {
|
|
234
|
+
super(message, { ...options, isRetryable: false });
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
function isTransientError(err) {
|
|
238
|
+
return err instanceof TheokitAgentError && err.isRetryable === true;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/internal/runtime/retry/with-retry.ts
|
|
242
|
+
function defaultSleep(ms, signal) {
|
|
243
|
+
return new Promise((resolve, reject) => {
|
|
244
|
+
if (signal?.aborted) {
|
|
245
|
+
reject(signal.reason instanceof Error ? signal.reason : new Error("withRetry: aborted"));
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const timer = setTimeout(() => {
|
|
249
|
+
signal?.removeEventListener("abort", onAbort);
|
|
250
|
+
resolve();
|
|
251
|
+
}, ms);
|
|
252
|
+
function onAbort() {
|
|
253
|
+
clearTimeout(timer);
|
|
254
|
+
reject(signal?.reason instanceof Error ? signal.reason : new Error("withRetry: aborted"));
|
|
255
|
+
}
|
|
256
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
function resolveRetryOptions(options) {
|
|
260
|
+
const retries = options?.retries ?? 3;
|
|
261
|
+
if (!Number.isInteger(retries) || retries < 0) {
|
|
262
|
+
throw new ConfigurationError(
|
|
263
|
+
`withRetry: retries must be a non-negative integer, got ${retries}`,
|
|
264
|
+
{ code: "invalid_retry_config" }
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
retries,
|
|
269
|
+
isRetryable: options?.isRetryable ?? isTransientError,
|
|
270
|
+
initialDelayMs: options?.initialDelayMs ?? 100,
|
|
271
|
+
maxDelayMs: options?.maxDelayMs ?? 3e4,
|
|
272
|
+
backoffMultiplier: options?.backoffMultiplier ?? 2,
|
|
273
|
+
rng: options?.rng ?? Math.random,
|
|
274
|
+
sleep: options?.sleep ?? defaultSleep,
|
|
275
|
+
signal: options?.signal
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function backoffMs(cfg, attempt) {
|
|
279
|
+
const ceiling = Math.min(cfg.maxDelayMs, cfg.initialDelayMs * cfg.backoffMultiplier ** attempt);
|
|
280
|
+
return Math.floor(cfg.rng() * ceiling);
|
|
281
|
+
}
|
|
282
|
+
async function withRetry(fn, options) {
|
|
283
|
+
const cfg = resolveRetryOptions(options);
|
|
284
|
+
let attempt = 0;
|
|
285
|
+
for (; ; ) {
|
|
286
|
+
try {
|
|
287
|
+
return await fn();
|
|
288
|
+
} catch (err) {
|
|
289
|
+
if (attempt >= cfg.retries || !cfg.isRetryable(err)) throw err;
|
|
290
|
+
await cfg.sleep(backoffMs(cfg, attempt), cfg.signal);
|
|
291
|
+
attempt += 1;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/retry.ts
|
|
297
|
+
var Retry = class {
|
|
298
|
+
constructor() {
|
|
299
|
+
}
|
|
300
|
+
static create(fn, options) {
|
|
301
|
+
return withRetry(fn, options);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// src/internal/providers/catalog-source-models-dev.ts
|
|
306
|
+
var DEFAULT_URL = "https://models.dev/api.json";
|
|
307
|
+
var TTL_MS = 60 * 60 * 1e3;
|
|
308
|
+
var FETCH_TIMEOUT_MS = 1e4;
|
|
309
|
+
function cachePathFor(url) {
|
|
310
|
+
const dir = path.join(os.homedir(), ".theokit", "cache", "models-dev");
|
|
311
|
+
if (url === DEFAULT_URL) return path.join(dir, "api.json");
|
|
312
|
+
const hash = crypto.createHash("sha256").update(url).digest("hex").slice(0, 12);
|
|
313
|
+
return path.join(dir, `api-${hash}.json`);
|
|
314
|
+
}
|
|
315
|
+
function writeCacheAtomic(path$1, body) {
|
|
316
|
+
fs.mkdirSync(path.dirname(path$1), { recursive: true });
|
|
317
|
+
const tmp = `${path$1}.tmp-${crypto.randomBytes(6).toString("hex")}`;
|
|
318
|
+
try {
|
|
319
|
+
fs.writeFileSync(tmp, body);
|
|
320
|
+
fs.renameSync(tmp, path$1);
|
|
321
|
+
} catch (err) {
|
|
322
|
+
try {
|
|
323
|
+
fs.unlinkSync(tmp);
|
|
324
|
+
} catch {
|
|
325
|
+
}
|
|
326
|
+
throw err;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function patchIndexFromApiJson(raw) {
|
|
330
|
+
if (typeof raw !== "object" || raw === null) return 0;
|
|
331
|
+
let patched = 0;
|
|
332
|
+
for (const [providerId, provider] of Object.entries(raw)) {
|
|
333
|
+
const models = provider?.models;
|
|
334
|
+
if (models === void 0 || typeof models !== "object") continue;
|
|
335
|
+
const profile = getProviderProfile(providerId);
|
|
336
|
+
if (profile === void 0) continue;
|
|
337
|
+
for (const [modelId, rawModel] of Object.entries(models)) {
|
|
338
|
+
const parsed = catalogModelSchema.safeParse(rawModel);
|
|
339
|
+
if (!parsed.success) continue;
|
|
340
|
+
patchModelInfo(`${profile.name}/${modelId}`, parsed.data);
|
|
341
|
+
patched++;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return patched;
|
|
345
|
+
}
|
|
346
|
+
function loadCacheIntoIndex(url = DEFAULT_URL) {
|
|
347
|
+
const path = cachePathFor(url);
|
|
348
|
+
let body;
|
|
349
|
+
try {
|
|
350
|
+
body = fs.readFileSync(path, "utf-8");
|
|
351
|
+
} catch {
|
|
352
|
+
return 0;
|
|
353
|
+
}
|
|
354
|
+
try {
|
|
355
|
+
return patchIndexFromApiJson(JSON.parse(body));
|
|
356
|
+
} catch {
|
|
357
|
+
try {
|
|
358
|
+
fs.unlinkSync(path);
|
|
359
|
+
} catch {
|
|
360
|
+
}
|
|
361
|
+
process.stderr.write(`[theokit-sdk] WARN: corrupt models-dev cache deleted (${path})
|
|
362
|
+
`);
|
|
363
|
+
return 0;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
async function refreshModelCatalog(opts = {}) {
|
|
367
|
+
if (process.env.THEOKIT_DISABLE_MODELS_FETCH !== void 0) {
|
|
368
|
+
return { source: "skipped", models: 0 };
|
|
369
|
+
}
|
|
370
|
+
const url = opts.url ?? process.env.THEOKIT_MODELS_URL ?? DEFAULT_URL;
|
|
371
|
+
const path = cachePathFor(url);
|
|
372
|
+
const now = opts.deps?.now ?? (() => Date.now());
|
|
373
|
+
if (opts.force !== true) {
|
|
374
|
+
try {
|
|
375
|
+
const age = now() - fs.statSync(path).mtimeMs;
|
|
376
|
+
if (age < TTL_MS) {
|
|
377
|
+
return { source: "cache", models: loadCacheIntoIndex(url) };
|
|
378
|
+
}
|
|
379
|
+
} catch {
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
const fetchImpl = opts.deps?.fetch ?? fetch;
|
|
383
|
+
let body;
|
|
384
|
+
try {
|
|
385
|
+
const res = await Retry.create(
|
|
386
|
+
async () => {
|
|
387
|
+
const r = await fetchImpl(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
|
|
388
|
+
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
389
|
+
return r;
|
|
390
|
+
},
|
|
391
|
+
// 2 transient retries with backoff (OpenCode does the same); every error here is worth one more try —
|
|
392
|
+
// the whole call is already fail-closed at the caller.
|
|
393
|
+
{ retries: 2, isRetryable: () => true, initialDelayMs: 200 }
|
|
394
|
+
);
|
|
395
|
+
body = await res.text();
|
|
396
|
+
JSON.parse(body);
|
|
397
|
+
} catch (err) {
|
|
398
|
+
process.stderr.write(
|
|
399
|
+
`[theokit-sdk] WARN: models-dev refresh failed (${err.message}) \u2014 serving existing data
|
|
400
|
+
`
|
|
401
|
+
);
|
|
402
|
+
return { source: "cache", models: loadCacheIntoIndex(url) };
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
writeCacheAtomic(path, body);
|
|
406
|
+
} catch (err) {
|
|
407
|
+
process.stderr.write(
|
|
408
|
+
`[theokit-sdk] WARN: models-dev cache write failed (${err.message})
|
|
409
|
+
`
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
return { source: "network", models: patchIndexFromApiJson(JSON.parse(body)) };
|
|
413
|
+
}
|
|
414
|
+
function getModelInfo(modelId) {
|
|
415
|
+
return getCatalogModelInfo(modelId);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
exports.getModelInfo = getModelInfo;
|
|
345
419
|
exports.humanizeModelName = humanizeModelName;
|
|
346
420
|
exports.parseModelId = parseModelId;
|
|
421
|
+
exports.refreshModelCatalog = refreshModelCatalog;
|
|
347
422
|
exports.resolveModelCapabilities = resolveModelCapabilities;
|
|
348
423
|
exports.toModelOption = toModelOption;
|
|
349
424
|
//# sourceMappingURL=models.cjs.map
|