@odla-ai/ai 0.2.2 → 0.3.1
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/README.md +71 -17
- package/dist/{anthropic-BIGCVQE4.js → anthropic-2NPMHJZT.js} +16 -8
- package/dist/anthropic-2NPMHJZT.js.map +1 -0
- package/dist/chunk-OX4TA4ZH.js +16 -0
- package/dist/chunk-OX4TA4ZH.js.map +1 -0
- package/dist/{chunk-QFT2DU2X.js → chunk-PXXCN2EU.js} +62 -5
- package/dist/chunk-PXXCN2EU.js.map +1 -0
- package/dist/{google-QY5B4T5O.js → google-3XV2VWVB.js} +18 -8
- package/dist/google-3XV2VWVB.js.map +1 -0
- package/dist/index.cjs +664 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +120 -10
- package/dist/index.d.ts +120 -10
- package/dist/index.js +545 -50
- package/dist/index.js.map +1 -1
- package/dist/{openai-PTZPJYTT.js → openai-NTQCF577.js} +20 -10
- package/dist/openai-NTQCF577.js.map +1 -0
- package/llms.txt +34 -15
- package/package.json +12 -5
- package/dist/anthropic-BIGCVQE4.js.map +0 -1
- package/dist/chunk-QFT2DU2X.js.map +0 -1
- package/dist/google-QY5B4T5O.js.map +0 -1
- package/dist/openai-PTZPJYTT.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AuthError,
|
|
3
|
+
CancelledError,
|
|
3
4
|
CapabilityError,
|
|
4
5
|
ConfigError,
|
|
5
6
|
ContextWindowError,
|
|
7
|
+
DeadlineExceededError,
|
|
6
8
|
InvalidRequestError,
|
|
7
9
|
OdlaAIError,
|
|
8
10
|
ProviderError,
|
|
9
11
|
RateLimitError,
|
|
12
|
+
ToolInputError,
|
|
10
13
|
addUsage,
|
|
11
14
|
emptyUsage,
|
|
12
|
-
normalizeError
|
|
13
|
-
|
|
15
|
+
normalizeError,
|
|
16
|
+
signalWithDeadline,
|
|
17
|
+
throwIfAborted
|
|
18
|
+
} from "./chunk-PXXCN2EU.js";
|
|
14
19
|
|
|
15
20
|
// src/shape/helpers.ts
|
|
16
21
|
function isTextBlock(b) {
|
|
@@ -44,8 +49,362 @@ function extractToolUses(content) {
|
|
|
44
49
|
return content.filter(isToolUseBlock);
|
|
45
50
|
}
|
|
46
51
|
|
|
52
|
+
// src/shape/json-schema-shape.ts
|
|
53
|
+
var ANNOTATION_KEYS = /* @__PURE__ */ new Set([
|
|
54
|
+
"$schema",
|
|
55
|
+
"$id",
|
|
56
|
+
"$anchor",
|
|
57
|
+
"title",
|
|
58
|
+
"description",
|
|
59
|
+
"default",
|
|
60
|
+
"examples",
|
|
61
|
+
"deprecated",
|
|
62
|
+
"readOnly",
|
|
63
|
+
"writeOnly",
|
|
64
|
+
"format"
|
|
65
|
+
]);
|
|
66
|
+
var SUPPORTED_KEYS = /* @__PURE__ */ new Set([
|
|
67
|
+
...ANNOTATION_KEYS,
|
|
68
|
+
"$ref",
|
|
69
|
+
"$defs",
|
|
70
|
+
"definitions",
|
|
71
|
+
"type",
|
|
72
|
+
"enum",
|
|
73
|
+
"const",
|
|
74
|
+
"allOf",
|
|
75
|
+
"anyOf",
|
|
76
|
+
"oneOf",
|
|
77
|
+
"not",
|
|
78
|
+
"properties",
|
|
79
|
+
"required",
|
|
80
|
+
"additionalProperties",
|
|
81
|
+
"minProperties",
|
|
82
|
+
"maxProperties",
|
|
83
|
+
"items",
|
|
84
|
+
"minItems",
|
|
85
|
+
"maxItems",
|
|
86
|
+
"uniqueItems",
|
|
87
|
+
"minLength",
|
|
88
|
+
"maxLength",
|
|
89
|
+
"pattern",
|
|
90
|
+
"minimum",
|
|
91
|
+
"maximum",
|
|
92
|
+
"exclusiveMinimum",
|
|
93
|
+
"exclusiveMaximum",
|
|
94
|
+
"multipleOf"
|
|
95
|
+
]);
|
|
96
|
+
var ALLOWED_TYPES = /* @__PURE__ */ new Set([
|
|
97
|
+
"null",
|
|
98
|
+
"boolean",
|
|
99
|
+
"object",
|
|
100
|
+
"array",
|
|
101
|
+
"number",
|
|
102
|
+
"integer",
|
|
103
|
+
"string"
|
|
104
|
+
]);
|
|
105
|
+
function validateSchemaShape(schema, path, root, issues) {
|
|
106
|
+
if (typeof schema === "boolean") return;
|
|
107
|
+
if (!isRecord(schema)) {
|
|
108
|
+
issue(issues, path, "schema must be an object or boolean");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
for (const key of Object.keys(schema)) {
|
|
112
|
+
if (!SUPPORTED_KEYS.has(key)) issue(issues, path, `schema uses unsupported keyword "${key}"`);
|
|
113
|
+
}
|
|
114
|
+
if (typeof schema.$ref === "string" && resolveLocalRef(root, schema.$ref) === void 0) {
|
|
115
|
+
issue(issues, path, `schema reference "${schema.$ref}" could not be resolved`);
|
|
116
|
+
} else if (schema.$ref !== void 0 && typeof schema.$ref !== "string") {
|
|
117
|
+
issue(issues, path, "schema $ref must be a string");
|
|
118
|
+
}
|
|
119
|
+
const types = typeof schema.type === "string" ? [schema.type] : Array.isArray(schema.type) ? schema.type : [];
|
|
120
|
+
if (schema.type !== void 0 && (types.length === 0 || !types.every((type) => typeof type === "string" && ALLOWED_TYPES.has(type)))) {
|
|
121
|
+
issue(issues, path, "schema type contains an unsupported JSON type");
|
|
122
|
+
}
|
|
123
|
+
if (schema.enum !== void 0 && !Array.isArray(schema.enum)) {
|
|
124
|
+
issue(issues, path, "schema enum must be an array");
|
|
125
|
+
}
|
|
126
|
+
if (schema.required !== void 0 && (!Array.isArray(schema.required) || !schema.required.every((key) => typeof key === "string"))) {
|
|
127
|
+
issue(issues, path, "schema required must be a string array");
|
|
128
|
+
}
|
|
129
|
+
validateConstraintShapes(schema, path, issues);
|
|
130
|
+
validateChildSchemas(schema, path, root, issues);
|
|
131
|
+
}
|
|
132
|
+
function validateChildSchemas(schema, path, root, issues) {
|
|
133
|
+
for (const key of ["properties", "$defs", "definitions"]) {
|
|
134
|
+
const group = schema[key];
|
|
135
|
+
if (group === void 0) continue;
|
|
136
|
+
if (!isRecord(group)) {
|
|
137
|
+
issue(issues, path, `schema ${key} must be an object`);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
for (const [name, child] of Object.entries(group)) {
|
|
141
|
+
validateSchemaShape(child, `${path}.${key}.${name}`, root, issues);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
for (const key of ["allOf", "anyOf", "oneOf"]) {
|
|
145
|
+
const group = schema[key];
|
|
146
|
+
if (group === void 0) continue;
|
|
147
|
+
if (!Array.isArray(group) || group.length === 0) {
|
|
148
|
+
issue(issues, path, `schema ${key} must be a non-empty array`);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
group.forEach((child, index2) => validateSchemaShape(child, `${path}.${key}[${index2}]`, root, issues));
|
|
152
|
+
}
|
|
153
|
+
for (const key of ["items", "not", "additionalProperties"]) {
|
|
154
|
+
const child = schema[key];
|
|
155
|
+
if (child !== void 0) validateSchemaShape(child, `${path}.${key}`, root, issues);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function validateConstraintShapes(schema, path, issues) {
|
|
159
|
+
for (const key of ["minLength", "maxLength", "minItems", "maxItems", "minProperties", "maxProperties"]) {
|
|
160
|
+
const value = schema[key];
|
|
161
|
+
if (value !== void 0 && (!Number.isInteger(value) || value < 0)) {
|
|
162
|
+
issue(issues, path, `schema ${key} must be a non-negative integer`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
for (const key of ["minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum"]) {
|
|
166
|
+
const value = schema[key];
|
|
167
|
+
if (value !== void 0 && !isFiniteNumber(value)) {
|
|
168
|
+
issue(issues, path, `schema ${key} must be a finite number`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (schema.multipleOf !== void 0 && (!isFiniteNumber(schema.multipleOf) || schema.multipleOf <= 0)) {
|
|
172
|
+
issue(issues, path, "schema multipleOf must be a finite number greater than zero");
|
|
173
|
+
}
|
|
174
|
+
if (schema.uniqueItems !== void 0 && typeof schema.uniqueItems !== "boolean") {
|
|
175
|
+
issue(issues, path, "schema uniqueItems must be a boolean");
|
|
176
|
+
}
|
|
177
|
+
validatePatternShape(schema.pattern, path, issues);
|
|
178
|
+
}
|
|
179
|
+
function validatePatternShape(value, path, issues) {
|
|
180
|
+
if (value === void 0) return;
|
|
181
|
+
if (typeof value !== "string") {
|
|
182
|
+
issue(issues, path, "schema pattern must be a string");
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
new RegExp(value, "u");
|
|
187
|
+
} catch {
|
|
188
|
+
issue(issues, path, `schema pattern "${value}" is invalid`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function resolveLocalRef(root, ref) {
|
|
192
|
+
if (ref === "#") return root;
|
|
193
|
+
if (!ref.startsWith("#/")) return void 0;
|
|
194
|
+
let current = root;
|
|
195
|
+
for (const raw of ref.slice(2).split("/")) {
|
|
196
|
+
const key = raw.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
197
|
+
if (!isRecord(current) || !Object.hasOwn(current, key)) return void 0;
|
|
198
|
+
current = current[key];
|
|
199
|
+
}
|
|
200
|
+
return current;
|
|
201
|
+
}
|
|
202
|
+
function isRecord(value) {
|
|
203
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
204
|
+
}
|
|
205
|
+
function isFiniteNumber(value) {
|
|
206
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
207
|
+
}
|
|
208
|
+
function issue(issues, path, message) {
|
|
209
|
+
issues.push({ path, message });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/shape/json-schema.ts
|
|
213
|
+
function validateJsonSchema(schema, value) {
|
|
214
|
+
const issues = [];
|
|
215
|
+
validateSchemaShape(schema, "$", schema, issues);
|
|
216
|
+
if (issues.length > 0) return { valid: false, issues };
|
|
217
|
+
visit(schema, value, "$", schema, issues, /* @__PURE__ */ new Set());
|
|
218
|
+
return { valid: issues.length === 0, issues };
|
|
219
|
+
}
|
|
220
|
+
function visit(schema, value, path, root, issues, refs) {
|
|
221
|
+
if (schema === true) return;
|
|
222
|
+
if (schema === false) {
|
|
223
|
+
issue2(issues, path, "is rejected by the schema");
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (!isRecord2(schema)) {
|
|
227
|
+
issue2(issues, path, "schema must be an object or boolean");
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
for (const key of Object.keys(schema)) {
|
|
231
|
+
if (!SUPPORTED_KEYS.has(key)) {
|
|
232
|
+
issue2(issues, path, `schema uses unsupported keyword "${key}"`);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (typeof schema.$ref === "string") {
|
|
237
|
+
const target = resolveLocalRef2(root, schema.$ref);
|
|
238
|
+
if (target === void 0) {
|
|
239
|
+
issue2(issues, path, `schema reference "${schema.$ref}" could not be resolved`);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (refs.has(schema.$ref)) {
|
|
243
|
+
issue2(issues, path, `cyclic schema reference "${schema.$ref}" is unsupported`);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const nextRefs = new Set(refs);
|
|
247
|
+
nextRefs.add(schema.$ref);
|
|
248
|
+
visit(target, value, path, root, issues, nextRefs);
|
|
249
|
+
}
|
|
250
|
+
if (Array.isArray(schema.enum) && !schema.enum.some((candidate) => jsonEqual(candidate, value))) {
|
|
251
|
+
issue2(issues, path, "must equal one of the allowed enum values");
|
|
252
|
+
}
|
|
253
|
+
if (Object.hasOwn(schema, "const") && !jsonEqual(schema.const, value)) {
|
|
254
|
+
issue2(issues, path, "must equal the constant value");
|
|
255
|
+
}
|
|
256
|
+
if (Array.isArray(schema.allOf)) {
|
|
257
|
+
for (const child of schema.allOf) visit(child, value, path, root, issues, refs);
|
|
258
|
+
}
|
|
259
|
+
if (Array.isArray(schema.anyOf) && !schema.anyOf.some((child) => matches(child, value, root, refs))) {
|
|
260
|
+
issue2(issues, path, "must match at least one anyOf schema");
|
|
261
|
+
}
|
|
262
|
+
if (Array.isArray(schema.oneOf)) {
|
|
263
|
+
const matchesCount = schema.oneOf.filter((child) => matches(child, value, root, refs)).length;
|
|
264
|
+
if (matchesCount !== 1) issue2(issues, path, "must match exactly one oneOf schema");
|
|
265
|
+
}
|
|
266
|
+
if (schema.not !== void 0 && matches(schema.not, value, root, refs)) {
|
|
267
|
+
issue2(issues, path, "must not match the forbidden schema");
|
|
268
|
+
}
|
|
269
|
+
const types = typeof schema.type === "string" ? [schema.type] : Array.isArray(schema.type) && schema.type.every((t) => typeof t === "string") ? schema.type : void 0;
|
|
270
|
+
if (schema.type !== void 0 && !types) {
|
|
271
|
+
issue2(issues, path, "schema type must be a string or string array");
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (types && !types.some((type) => isType(value, type))) {
|
|
275
|
+
issue2(issues, path, `must be ${types.join(" or ")}`);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (typeof value === "string") validateString(schema, value, path, issues);
|
|
279
|
+
if (typeof value === "number") validateNumber(schema, value, path, issues);
|
|
280
|
+
if (Array.isArray(value)) validateArray(schema, value, path, root, issues, refs);
|
|
281
|
+
if (isRecord2(value)) validateObject(schema, value, path, root, issues, refs);
|
|
282
|
+
}
|
|
283
|
+
function validateString(schema, value, path, issues) {
|
|
284
|
+
if (isFiniteNumber2(schema.minLength) && [...value].length < schema.minLength) issue2(issues, path, `must have at least ${schema.minLength} characters`);
|
|
285
|
+
if (isFiniteNumber2(schema.maxLength) && [...value].length > schema.maxLength) issue2(issues, path, `must have at most ${schema.maxLength} characters`);
|
|
286
|
+
if (typeof schema.pattern === "string") {
|
|
287
|
+
try {
|
|
288
|
+
if (!new RegExp(schema.pattern, "u").test(value)) issue2(issues, path, `must match pattern ${schema.pattern}`);
|
|
289
|
+
} catch {
|
|
290
|
+
issue2(issues, path, `schema pattern "${schema.pattern}" is invalid`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function validateNumber(schema, value, path, issues) {
|
|
295
|
+
if (!Number.isFinite(value)) {
|
|
296
|
+
issue2(issues, path, "must be a finite number");
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (isFiniteNumber2(schema.minimum) && value < schema.minimum) issue2(issues, path, `must be >= ${schema.minimum}`);
|
|
300
|
+
if (isFiniteNumber2(schema.maximum) && value > schema.maximum) issue2(issues, path, `must be <= ${schema.maximum}`);
|
|
301
|
+
if (isFiniteNumber2(schema.exclusiveMinimum) && value <= schema.exclusiveMinimum) issue2(issues, path, `must be > ${schema.exclusiveMinimum}`);
|
|
302
|
+
if (isFiniteNumber2(schema.exclusiveMaximum) && value >= schema.exclusiveMaximum) issue2(issues, path, `must be < ${schema.exclusiveMaximum}`);
|
|
303
|
+
if (isFiniteNumber2(schema.multipleOf) && schema.multipleOf > 0) {
|
|
304
|
+
const quotient = value / schema.multipleOf;
|
|
305
|
+
if (Math.abs(quotient - Math.round(quotient)) > Number.EPSILON * Math.max(1, Math.abs(quotient))) {
|
|
306
|
+
issue2(issues, path, `must be a multiple of ${schema.multipleOf}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function validateArray(schema, value, path, root, issues, refs) {
|
|
311
|
+
if (isFiniteNumber2(schema.minItems) && value.length < schema.minItems) issue2(issues, path, `must contain at least ${schema.minItems} items`);
|
|
312
|
+
if (isFiniteNumber2(schema.maxItems) && value.length > schema.maxItems) issue2(issues, path, `must contain at most ${schema.maxItems} items`);
|
|
313
|
+
if (schema.uniqueItems === true) {
|
|
314
|
+
for (let i = 0; i < value.length; i++) {
|
|
315
|
+
if (value.slice(0, i).some((other) => jsonEqual(other, value[i]))) {
|
|
316
|
+
issue2(issues, `${path}[${i}]`, "must be unique");
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (schema.items !== void 0) {
|
|
322
|
+
for (let i = 0; i < value.length; i++) visit(schema.items, value[i], `${path}[${i}]`, root, issues, refs);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function validateObject(schema, value, path, root, issues, refs) {
|
|
326
|
+
const keys = Object.keys(value);
|
|
327
|
+
if (isFiniteNumber2(schema.minProperties) && keys.length < schema.minProperties) issue2(issues, path, `must contain at least ${schema.minProperties} properties`);
|
|
328
|
+
if (isFiniteNumber2(schema.maxProperties) && keys.length > schema.maxProperties) issue2(issues, path, `must contain at most ${schema.maxProperties} properties`);
|
|
329
|
+
if (schema.required !== void 0 && (!Array.isArray(schema.required) || !schema.required.every((key) => typeof key === "string"))) {
|
|
330
|
+
issue2(issues, path, "schema required must be a string array");
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
for (const key of schema.required ?? []) {
|
|
334
|
+
if (!Object.hasOwn(value, key)) issue2(issues, childPath(path, key), "is required");
|
|
335
|
+
}
|
|
336
|
+
const properties = isRecord2(schema.properties) ? schema.properties : {};
|
|
337
|
+
for (const [key, child] of Object.entries(properties)) {
|
|
338
|
+
if (Object.hasOwn(value, key)) visit(child, value[key], childPath(path, key), root, issues, refs);
|
|
339
|
+
}
|
|
340
|
+
const extras = keys.filter((key) => !Object.hasOwn(properties, key));
|
|
341
|
+
if (schema.additionalProperties === false) {
|
|
342
|
+
for (const key of extras) issue2(issues, childPath(path, key), "is not an allowed property");
|
|
343
|
+
} else if (schema.additionalProperties !== void 0 && schema.additionalProperties !== true) {
|
|
344
|
+
for (const key of extras) visit(schema.additionalProperties, value[key], childPath(path, key), root, issues, refs);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function matches(schema, value, root, refs) {
|
|
348
|
+
const nested = [];
|
|
349
|
+
visit(schema, value, "$", root, nested, refs);
|
|
350
|
+
return nested.length === 0;
|
|
351
|
+
}
|
|
352
|
+
function resolveLocalRef2(root, ref) {
|
|
353
|
+
if (ref === "#") return root;
|
|
354
|
+
if (!ref.startsWith("#/")) return void 0;
|
|
355
|
+
let current = root;
|
|
356
|
+
for (const raw of ref.slice(2).split("/")) {
|
|
357
|
+
const key = raw.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
358
|
+
if (!isRecord2(current) || !Object.hasOwn(current, key)) return void 0;
|
|
359
|
+
current = current[key];
|
|
360
|
+
}
|
|
361
|
+
return current;
|
|
362
|
+
}
|
|
363
|
+
function isRecord2(value) {
|
|
364
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
365
|
+
}
|
|
366
|
+
function isType(value, type) {
|
|
367
|
+
switch (type) {
|
|
368
|
+
case "null":
|
|
369
|
+
return value === null;
|
|
370
|
+
case "boolean":
|
|
371
|
+
return typeof value === "boolean";
|
|
372
|
+
case "object":
|
|
373
|
+
return isRecord2(value);
|
|
374
|
+
case "array":
|
|
375
|
+
return Array.isArray(value);
|
|
376
|
+
case "number":
|
|
377
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
378
|
+
case "integer":
|
|
379
|
+
return typeof value === "number" && Number.isInteger(value);
|
|
380
|
+
case "string":
|
|
381
|
+
return typeof value === "string";
|
|
382
|
+
default:
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function isFiniteNumber2(value) {
|
|
387
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
388
|
+
}
|
|
389
|
+
function jsonEqual(a, b) {
|
|
390
|
+
if (Object.is(a, b)) return true;
|
|
391
|
+
if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((v, i) => jsonEqual(v, b[i]));
|
|
392
|
+
if (isRecord2(a) && isRecord2(b)) {
|
|
393
|
+
const aKeys = Object.keys(a);
|
|
394
|
+
const bKeys = Object.keys(b);
|
|
395
|
+
return aKeys.length === bKeys.length && aKeys.every((key) => Object.hasOwn(b, key) && jsonEqual(a[key], b[key]));
|
|
396
|
+
}
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
function childPath(path, key) {
|
|
400
|
+
return /^[A-Za-z_$][\w$]*$/.test(key) ? `${path}.${key}` : `${path}[${JSON.stringify(key)}]`;
|
|
401
|
+
}
|
|
402
|
+
function issue2(issues, path, message) {
|
|
403
|
+
issues.push({ path, message });
|
|
404
|
+
}
|
|
405
|
+
|
|
47
406
|
// src/shape/capabilities.ts
|
|
48
|
-
function chatCapabilities(
|
|
407
|
+
function chatCapabilities(overrides2 = {}) {
|
|
49
408
|
return {
|
|
50
409
|
kind: "chat",
|
|
51
410
|
textIn: true,
|
|
@@ -58,7 +417,7 @@ function chatCapabilities(overrides = {}) {
|
|
|
58
417
|
streaming: true,
|
|
59
418
|
structuredOutput: true,
|
|
60
419
|
webSearch: false,
|
|
61
|
-
...
|
|
420
|
+
...overrides2
|
|
62
421
|
};
|
|
63
422
|
}
|
|
64
423
|
function validateRequest(spec, req) {
|
|
@@ -147,6 +506,15 @@ var OPENAI_MODELS = [
|
|
|
147
506
|
capabilities: chatCapabilities({ effort: true }),
|
|
148
507
|
contextWindow: 4e5
|
|
149
508
|
},
|
|
509
|
+
{
|
|
510
|
+
// Compact GPT-5.4 reasoning model (effort knob), same 400K context as the
|
|
511
|
+
// rest of the GPT-5 line. The default for odla-o11y AI triage.
|
|
512
|
+
id: "gpt-5.4-mini",
|
|
513
|
+
provider: "openai",
|
|
514
|
+
nativeId: "gpt-5.4-mini",
|
|
515
|
+
capabilities: chatCapabilities({ effort: true }),
|
|
516
|
+
contextWindow: 4e5
|
|
517
|
+
},
|
|
150
518
|
{
|
|
151
519
|
id: "gpt-5",
|
|
152
520
|
provider: "openai",
|
|
@@ -264,37 +632,60 @@ function providersInCatalog(catalog) {
|
|
|
264
632
|
}
|
|
265
633
|
|
|
266
634
|
// src/providers/registry.ts
|
|
635
|
+
var CLIENT_TTL_MS = 6e4;
|
|
267
636
|
var cache = /* @__PURE__ */ new Map();
|
|
637
|
+
var inflight = /* @__PURE__ */ new Map();
|
|
638
|
+
var overrides = /* @__PURE__ */ new Map();
|
|
268
639
|
async function getProvider(id, apiKey) {
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
640
|
+
const override = overrides.get(id);
|
|
641
|
+
if (override?.apiKey === apiKey) return override.provider;
|
|
642
|
+
const fingerprint = await fingerprintKey(apiKey);
|
|
643
|
+
const existing = cache.get(id);
|
|
644
|
+
if (existing?.fingerprint === fingerprint && existing.expiresAt > Date.now()) return existing.provider;
|
|
645
|
+
if (existing) cache.delete(id);
|
|
646
|
+
const pending = inflight.get(id);
|
|
647
|
+
if (pending?.fingerprint === fingerprint) return pending.promise;
|
|
648
|
+
const promise = createProvider(id, apiKey).then((provider) => {
|
|
649
|
+
cache.set(id, { fingerprint, provider, expiresAt: Date.now() + CLIENT_TTL_MS });
|
|
650
|
+
return provider;
|
|
651
|
+
}).finally(() => {
|
|
652
|
+
if (inflight.get(id)?.promise === promise) inflight.delete(id);
|
|
653
|
+
});
|
|
654
|
+
inflight.set(id, { fingerprint, promise });
|
|
655
|
+
return promise;
|
|
656
|
+
}
|
|
657
|
+
async function createProvider(id, apiKey) {
|
|
272
658
|
let provider;
|
|
273
659
|
switch (id) {
|
|
274
660
|
case "anthropic": {
|
|
275
|
-
const { AnthropicProvider } = await import("./anthropic-
|
|
661
|
+
const { AnthropicProvider } = await import("./anthropic-2NPMHJZT.js");
|
|
276
662
|
provider = new AnthropicProvider(apiKey);
|
|
277
663
|
break;
|
|
278
664
|
}
|
|
279
665
|
case "openai": {
|
|
280
|
-
const { OpenAIProvider } = await import("./openai-
|
|
666
|
+
const { OpenAIProvider } = await import("./openai-NTQCF577.js");
|
|
281
667
|
provider = new OpenAIProvider(apiKey);
|
|
282
668
|
break;
|
|
283
669
|
}
|
|
284
670
|
case "google": {
|
|
285
|
-
const { GoogleProvider } = await import("./google-
|
|
671
|
+
const { GoogleProvider } = await import("./google-3XV2VWVB.js");
|
|
286
672
|
provider = new GoogleProvider(apiKey);
|
|
287
673
|
break;
|
|
288
674
|
}
|
|
289
675
|
}
|
|
290
|
-
cache.set(cacheKey, provider);
|
|
291
676
|
return provider;
|
|
292
677
|
}
|
|
678
|
+
async function fingerprintKey(apiKey) {
|
|
679
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(apiKey));
|
|
680
|
+
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
681
|
+
}
|
|
293
682
|
function registerProvider(id, apiKey, provider) {
|
|
294
|
-
|
|
683
|
+
overrides.set(id, { apiKey, provider });
|
|
295
684
|
}
|
|
296
685
|
function clearProviderCache() {
|
|
297
686
|
cache.clear();
|
|
687
|
+
inflight.clear();
|
|
688
|
+
overrides.clear();
|
|
298
689
|
}
|
|
299
690
|
|
|
300
691
|
// src/client/keys.ts
|
|
@@ -364,7 +755,9 @@ function init(opts = {}) {
|
|
|
364
755
|
messages: [{ role: "user", content: c.user }],
|
|
365
756
|
tools: [{ name: c.tool.name, description: c.tool.description ?? "", inputSchema: c.tool.parameters }],
|
|
366
757
|
toolChoice: { type: "tool", name: c.tool.name },
|
|
367
|
-
maxTokens: c.maxTokens ?? 4096
|
|
758
|
+
maxTokens: c.maxTokens ?? 4096,
|
|
759
|
+
signal: c.signal,
|
|
760
|
+
deadline: c.deadline
|
|
368
761
|
};
|
|
369
762
|
const { spec, provider } = await resolveProviderFor(model);
|
|
370
763
|
validateRequest(spec, req);
|
|
@@ -373,6 +766,11 @@ function init(opts = {}) {
|
|
|
373
766
|
if (!block || block.type !== "tool_use") {
|
|
374
767
|
throw new ProviderError(`Model "${model}" did not return the forced tool call "${c.tool.name}".`);
|
|
375
768
|
}
|
|
769
|
+
const validation = validateJsonSchema(c.tool.parameters, block.input);
|
|
770
|
+
if (!validation.valid) {
|
|
771
|
+
const detail = validation.issues.slice(0, 5).map((i) => `${i.path} ${i.message}`).join("; ");
|
|
772
|
+
throw new ToolInputError(`Tool "${c.tool.name}" returned invalid input: ${detail}`, { tool: c.tool.name });
|
|
773
|
+
}
|
|
376
774
|
return { value: block.input, usage: res.usage };
|
|
377
775
|
};
|
|
378
776
|
const search = async (c) => {
|
|
@@ -382,7 +780,9 @@ function init(opts = {}) {
|
|
|
382
780
|
system: c.system,
|
|
383
781
|
messages: [{ role: "user", content: c.user }],
|
|
384
782
|
webSearch: true,
|
|
385
|
-
maxTokens: c.maxTokens ?? 8192
|
|
783
|
+
maxTokens: c.maxTokens ?? 8192,
|
|
784
|
+
signal: c.signal,
|
|
785
|
+
deadline: c.deadline
|
|
386
786
|
};
|
|
387
787
|
const { spec, provider } = await resolveProviderFor(model);
|
|
388
788
|
validateRequest(spec, req);
|
|
@@ -408,20 +808,20 @@ function generateLlmsTxt(catalog = DEFAULT_CATALOG) {
|
|
|
408
808
|
const models = Object.values(catalog).map((s) => `- \`${s.id}\` (${s.provider}) \u2014 ${capsSummary(s.capabilities)}`).join("\n");
|
|
409
809
|
return `# @odla-ai/ai \u2014 LLM context
|
|
410
810
|
|
|
411
|
-
>
|
|
412
|
-
>
|
|
413
|
-
>
|
|
811
|
+
> EARLY ACCESS \u2014 pre-1.0. Agents work from bounded runbooks; humans approve
|
|
812
|
+
> credentials, production changes, releases, and merges. APIs and exact package
|
|
813
|
+
> availability can change. Review documented guarantees and limitations; this
|
|
814
|
+
> software is MIT-licensed and provided without warranty.
|
|
414
815
|
|
|
415
816
|
One general-purpose interface for AI inference across Anthropic (Claude), OpenAI
|
|
416
817
|
(GPT), and Google (Gemini) \u2014 text, image, and audio \u2014 plus a tool-use agent
|
|
417
818
|
engine and an eval harness. Library-only: import in-process, bring your own keys.
|
|
418
|
-
|
|
819
|
+
Targets Node 20+ and Cloudflare Workers. Provider adapters are runtime-lazy, but
|
|
820
|
+
all provider SDKs are package dependencies. Never expose long-lived keys in a browser.
|
|
419
821
|
|
|
420
822
|
## Install
|
|
421
823
|
|
|
422
824
|
npm i @odla-ai/ai
|
|
423
|
-
# provider SDKs are peer-lazy-loaded; install those you use:
|
|
424
|
-
# @anthropic-ai/sdk openai @google/genai
|
|
425
825
|
# optional, for odla-db-backed storage + secrets:
|
|
426
826
|
# @odla-ai/db
|
|
427
827
|
|
|
@@ -435,7 +835,8 @@ Isomorphic (Node 20+, Cloudflare Workers, browser). Provider SDKs are lazy-loade
|
|
|
435
835
|
- \`ai.stream(input)\` \u2192 async iterable of normalized events: message_start \u2192
|
|
436
836
|
content_block_start \u2192 content_block_delta {text_delta|thinking_delta|input_json_delta}
|
|
437
837
|
\u2192 content_block_stop \u2192 message_delta \u2192 message_stop (plus \`error\`).
|
|
438
|
-
- \`ai.extract<T>({ model, user, tool })\` \u2192 forced tool call; returns
|
|
838
|
+
- \`ai.extract<T>({ model, user, tool })\` \u2192 forced tool call; returns an object
|
|
839
|
+
validated against the declared JSON Schema. Unsupported assertions fail closed.
|
|
439
840
|
- \`ai.search({ model, user })\` \u2192 web-search-grounded prose.
|
|
440
841
|
|
|
441
842
|
## Content blocks (multimodal)
|
|
@@ -454,7 +855,15 @@ audio block to a Claude model throws CapabilityError before any network call.
|
|
|
454
855
|
// run.finalText, run.toolCalls, run.usage, run.steps, run.stoppedReason
|
|
455
856
|
|
|
456
857
|
Tools may carry taint labels (outputTaint / acceptsTaint) for a CaMeL-style
|
|
457
|
-
prompt-injection gate. Personas may carry a MemoryScope.
|
|
858
|
+
prompt-injection gate. Personas may carry a MemoryScope. Tool inputs are schema-
|
|
859
|
+
validated before handlers run. Pass signal/deadline and a run budget
|
|
860
|
+
({maxInputTokens,maxOutputTokens,maxTotalTokens,maxToolCalls}) to bound follow-up
|
|
861
|
+
effects and requested output. Input and total limits are post-turn ceilings:
|
|
862
|
+
provider usage is unavailable before the first response, so they cannot
|
|
863
|
+
tokenizer-bound the first prompt. Output and remaining-total limits cap the next
|
|
864
|
+
request's maxTokens. Token limits must be positive integers; maxToolCalls may be
|
|
865
|
+
zero. Budget exits pair every requested tool_use with an error tool_result before
|
|
866
|
+
memory is persisted.
|
|
458
867
|
|
|
459
868
|
## Skills
|
|
460
869
|
|
|
@@ -481,14 +890,17 @@ device-authorization handshake for a scoped, revocable token, then provisions.
|
|
|
481
890
|
import { requestToken, init as odlaInit } from "@odla-ai/db";
|
|
482
891
|
import { provisionAgentApp, odlaDbKeyResolver, OdlaDbMemory, persistRun, init } from "@odla-ai/ai";
|
|
483
892
|
|
|
484
|
-
|
|
485
|
-
const
|
|
893
|
+
const ODLA_PLATFORM = "https://odla.ai";
|
|
894
|
+
const ODLA_DB_URL = "https://db.odla.ai";
|
|
895
|
+
|
|
896
|
+
// 1. Handshake: the human approves a short code at odla.ai.
|
|
897
|
+
const { token } = await requestToken({ endpoint: ODLA_PLATFORM, onCode: ({ userCode }) => show(userCode) });
|
|
486
898
|
|
|
487
899
|
// 2. Provision: create app, mint app key, push agent schema, store BYO provider keys as secrets.
|
|
488
|
-
const { appId, appKey } = await provisionAgentApp({ endpoint:
|
|
900
|
+
const { appId, appKey } = await provisionAgentApp({ endpoint: ODLA_PLATFORM, token, providerKeys: { openai: OPENAI_KEY } });
|
|
489
901
|
|
|
490
902
|
// 3. Runtime: read keys from odla-db secrets; persist memory + run traces.
|
|
491
|
-
const db = odlaInit({ appId, adminToken: appKey, endpoint:
|
|
903
|
+
const db = odlaInit({ appId, adminToken: appKey, endpoint: ODLA_DB_URL });
|
|
492
904
|
const ai = init({ resolveKey: odlaDbKeyResolver(db) });
|
|
493
905
|
const persona = { name: "assistant", model: "gpt-5", memory: new OdlaDbMemory({ db, sessionId: "user-42" }) };
|
|
494
906
|
const run = await runAgent(ai, persona, { input: "hello" });
|
|
@@ -514,16 +926,22 @@ env's odla-db tenant secrets, write-only for operators). One call reads both:
|
|
|
514
926
|
platform: "https://odla.ai", appId: APP_ID, env: "prod", db });
|
|
515
927
|
// ai.chat / ai.stream / ai.extract \u2014 provider/model from public-config
|
|
516
928
|
// (cached ~60s, so Studio changes apply without redeploys); the key is
|
|
517
|
-
// fetched from the vault
|
|
929
|
+
// fetched from the vault via a finite-TTL odlaDbKeyResolver.
|
|
518
930
|
|
|
519
931
|
The only secret the Worker carries is its odla-db app key. Rotating the LLM
|
|
520
|
-
key = writing the secret again;
|
|
932
|
+
key = writing the secret again; the default warm-isolate key TTL is 60 seconds
|
|
933
|
+
and cacheVersion provides immediate invalidation. Provider SDK clients are
|
|
934
|
+
fingerprinted, bounded to one generation per provider, and expire after 60
|
|
935
|
+
seconds. The platform provider is enforced: models from other providers are
|
|
936
|
+
absent from the facade catalog. If no default model is configured, each call
|
|
937
|
+
must name a model for that provider. Only public config is shared; tenant-bound
|
|
938
|
+
Ai facades are not cached globally.
|
|
521
939
|
|
|
522
940
|
## Errors
|
|
523
941
|
|
|
524
942
|
Every error is an OdlaAIError subclass with a stable \`code\` \u2014 branch on err.code:
|
|
525
943
|
config, auth, rate_limit, capability_unsupported, context_window, invalid_request,
|
|
526
|
-
tool_input_invalid, provider_error.
|
|
944
|
+
tool_input_invalid, cancelled, deadline_exceeded, provider_error.
|
|
527
945
|
|
|
528
946
|
## Models
|
|
529
947
|
|
|
@@ -573,6 +991,7 @@ function assertSinkAcceptsTaint(sink, allowed, actual) {
|
|
|
573
991
|
|
|
574
992
|
// src/agent/loop.ts
|
|
575
993
|
async function runAgent(inference, persona, input) {
|
|
994
|
+
validateRunLimits(persona.maxSteps, input.budget);
|
|
576
995
|
const { system, tools } = composeSkills(persona.system, persona.tools, persona.skills);
|
|
577
996
|
const handlerByName = /* @__PURE__ */ new Map();
|
|
578
997
|
for (const t of tools) if (t.handler) handlerByName.set(t.name, t);
|
|
@@ -584,21 +1003,26 @@ async function runAgent(inference, persona, input) {
|
|
|
584
1003
|
const toolCalls = [];
|
|
585
1004
|
const accumulatedTaint = /* @__PURE__ */ new Set(["llm_inherited"]);
|
|
586
1005
|
const maxSteps = persona.maxSteps ?? 8;
|
|
1006
|
+
const runSignal = signalWithDeadline(input.signal, input.deadline);
|
|
587
1007
|
let response;
|
|
588
1008
|
let stoppedReason = "max_steps";
|
|
589
1009
|
let steps = 0;
|
|
1010
|
+
let attemptedToolCalls = 0;
|
|
590
1011
|
while (steps < maxSteps) {
|
|
591
1012
|
steps++;
|
|
1013
|
+
const remainingOutput = remainingOutputTokens(usage, input.budget);
|
|
592
1014
|
response = await inference.chat({
|
|
593
1015
|
model: persona.model,
|
|
594
1016
|
system,
|
|
595
1017
|
messages,
|
|
596
1018
|
tools: tools.length > 0 ? tools.map(toOracleTool) : void 0,
|
|
597
|
-
maxTokens: persona.maxTokens ?? 4096,
|
|
1019
|
+
maxTokens: Math.min(persona.maxTokens ?? 4096, remainingOutput ?? Number.POSITIVE_INFINITY),
|
|
598
1020
|
effort: persona.effort,
|
|
599
1021
|
thinking: persona.thinking,
|
|
600
1022
|
temperature: persona.temperature,
|
|
601
|
-
webSearch: persona.webSearch
|
|
1023
|
+
webSearch: persona.webSearch,
|
|
1024
|
+
signal: runSignal,
|
|
1025
|
+
deadline: input.deadline
|
|
602
1026
|
});
|
|
603
1027
|
addUsage(usage, response.usage);
|
|
604
1028
|
messages.push({ role: "assistant", content: response.content });
|
|
@@ -611,8 +1035,22 @@ async function runAgent(inference, persona, input) {
|
|
|
611
1035
|
stoppedReason = "end_turn";
|
|
612
1036
|
break;
|
|
613
1037
|
}
|
|
1038
|
+
if (budgetExceeded(usage, input.budget)) {
|
|
1039
|
+
stoppedReason = "budget_exhausted";
|
|
1040
|
+
messages.push({
|
|
1041
|
+
role: "user",
|
|
1042
|
+
content: toolUses.map((toolUse) => errorResult(toolUse.id, "Run budget exhausted before tool execution."))
|
|
1043
|
+
});
|
|
1044
|
+
break;
|
|
1045
|
+
}
|
|
614
1046
|
const resultBlocks = [];
|
|
615
1047
|
for (const toolUse of toolUses) {
|
|
1048
|
+
if (input.budget?.maxToolCalls !== void 0 && attemptedToolCalls >= input.budget.maxToolCalls) {
|
|
1049
|
+
stoppedReason = "budget_exhausted";
|
|
1050
|
+
resultBlocks.push(errorResult(toolUse.id, "Run tool-call budget exhausted before execution."));
|
|
1051
|
+
continue;
|
|
1052
|
+
}
|
|
1053
|
+
attemptedToolCalls++;
|
|
616
1054
|
const def = handlerByName.get(toolUse.name);
|
|
617
1055
|
if (!def || !def.handler) {
|
|
618
1056
|
resultBlocks.push(errorResult(toolUse.id, `No local handler for tool "${toolUse.name}".`));
|
|
@@ -620,16 +1058,26 @@ async function runAgent(inference, persona, input) {
|
|
|
620
1058
|
}
|
|
621
1059
|
if (def.acceptsTaint) assertSinkAcceptsTaint(def.name, def.acceptsTaint, accumulatedTaint);
|
|
622
1060
|
let output;
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
output = { content: `Tool "${toolUse.name}"
|
|
1061
|
+
const validation = validateJsonSchema(def.inputSchema, toolUse.input);
|
|
1062
|
+
if (!validation.valid) {
|
|
1063
|
+
const detail = validation.issues.slice(0, 5).map((i) => `${i.path} ${i.message}`).join("; ");
|
|
1064
|
+
output = { content: `Tool "${toolUse.name}" input is invalid: ${detail}`, isError: true };
|
|
1065
|
+
} else {
|
|
1066
|
+
throwIfAborted(runSignal);
|
|
1067
|
+
try {
|
|
1068
|
+
output = await def.handler(toolUse.input, { taint: new Set(accumulatedTaint), signal: runSignal });
|
|
1069
|
+
} catch (error) {
|
|
1070
|
+
if (error instanceof CancelledError || error instanceof DeadlineExceededError) throw error;
|
|
1071
|
+
throwIfAborted(runSignal);
|
|
1072
|
+
output = { content: `Tool "${toolUse.name}" failed.`, isError: true };
|
|
1073
|
+
}
|
|
627
1074
|
}
|
|
628
1075
|
toolCalls.push({ toolUse, output });
|
|
629
1076
|
resultBlocks.push({ type: "tool_result", toolUseId: toolUse.id, content: output.content, isError: output.isError });
|
|
630
1077
|
for (const label of def.outputTaint ?? []) accumulatedTaint.add(label);
|
|
631
1078
|
}
|
|
632
1079
|
messages.push({ role: "user", content: resultBlocks });
|
|
1080
|
+
if (stoppedReason === "budget_exhausted") break;
|
|
633
1081
|
}
|
|
634
1082
|
if (!response) throw new Error("runAgent: maxSteps must be at least 1");
|
|
635
1083
|
if (persona.memory) await persona.memory.append(messages.slice(runStartIndex));
|
|
@@ -643,6 +1091,36 @@ async function runAgent(inference, persona, input) {
|
|
|
643
1091
|
stoppedReason
|
|
644
1092
|
};
|
|
645
1093
|
}
|
|
1094
|
+
function budgetExceeded(usage, budget) {
|
|
1095
|
+
if (!budget) return false;
|
|
1096
|
+
if (budget.maxInputTokens !== void 0 && usage.inputTokens >= budget.maxInputTokens) return true;
|
|
1097
|
+
if (budget.maxOutputTokens !== void 0 && usage.outputTokens >= budget.maxOutputTokens) return true;
|
|
1098
|
+
if (budget.maxTotalTokens !== void 0 && usage.inputTokens + usage.outputTokens >= budget.maxTotalTokens) return true;
|
|
1099
|
+
return false;
|
|
1100
|
+
}
|
|
1101
|
+
function remainingOutputTokens(usage, budget) {
|
|
1102
|
+
if (!budget) return void 0;
|
|
1103
|
+
const remaining = [
|
|
1104
|
+
budget.maxOutputTokens === void 0 ? void 0 : budget.maxOutputTokens - usage.outputTokens,
|
|
1105
|
+
budget.maxTotalTokens === void 0 ? void 0 : budget.maxTotalTokens - usage.inputTokens - usage.outputTokens
|
|
1106
|
+
].filter((value) => value !== void 0);
|
|
1107
|
+
return remaining.length === 0 ? void 0 : Math.max(1, Math.min(...remaining));
|
|
1108
|
+
}
|
|
1109
|
+
function validateRunLimits(maxSteps, budget) {
|
|
1110
|
+
if (maxSteps !== void 0 && (!Number.isInteger(maxSteps) || maxSteps < 1)) {
|
|
1111
|
+
throw new ConfigError("persona.maxSteps must be a positive integer.");
|
|
1112
|
+
}
|
|
1113
|
+
if (!budget) return;
|
|
1114
|
+
for (const key of ["maxInputTokens", "maxOutputTokens", "maxTotalTokens"]) {
|
|
1115
|
+
const value = budget[key];
|
|
1116
|
+
if (value !== void 0 && (!Number.isInteger(value) || value < 1)) {
|
|
1117
|
+
throw new ConfigError(`budget.${key} must be a positive integer.`);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
if (budget.maxToolCalls !== void 0 && (!Number.isInteger(budget.maxToolCalls) || budget.maxToolCalls < 0)) {
|
|
1121
|
+
throw new ConfigError("budget.maxToolCalls must be a non-negative integer.");
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
646
1124
|
function errorResult(toolUseId, message) {
|
|
647
1125
|
return { type: "tool_result", toolUseId, content: message, isError: true };
|
|
648
1126
|
}
|
|
@@ -945,22 +1423,32 @@ var DEFAULT_SECRET_NAMES = {
|
|
|
945
1423
|
};
|
|
946
1424
|
function odlaDbKeyResolver(db, opts = {}) {
|
|
947
1425
|
const nameFor = opts.secretName ?? ((p) => DEFAULT_SECRET_NAMES[p]);
|
|
948
|
-
const
|
|
1426
|
+
const ttlMs = opts.cache === false ? 0 : Math.max(0, opts.ttlMs ?? 6e4);
|
|
949
1427
|
const cache3 = /* @__PURE__ */ new Map();
|
|
950
1428
|
return async (provider) => {
|
|
951
|
-
|
|
1429
|
+
const version = typeof opts.cacheVersion === "function" ? opts.cacheVersion(provider) : opts.cacheVersion ?? "";
|
|
1430
|
+
if (ttlMs > 0) {
|
|
952
1431
|
const hit = cache3.get(provider);
|
|
953
|
-
if (hit
|
|
1432
|
+
if (hit?.version === version && hit.expiresAt > Date.now()) return hit.value;
|
|
1433
|
+
cache3.delete(provider);
|
|
954
1434
|
}
|
|
955
1435
|
try {
|
|
956
1436
|
const key = await db.secrets.get(nameFor(provider));
|
|
957
|
-
if (
|
|
1437
|
+
if (ttlMs > 0) cache3.set(provider, { version, value: key, expiresAt: Date.now() + ttlMs });
|
|
958
1438
|
return key;
|
|
959
|
-
} catch {
|
|
960
|
-
return void 0;
|
|
1439
|
+
} catch (error) {
|
|
1440
|
+
if (isMissingSecret(error)) return void 0;
|
|
1441
|
+
throw error;
|
|
961
1442
|
}
|
|
962
1443
|
};
|
|
963
1444
|
}
|
|
1445
|
+
function isMissingSecret(error) {
|
|
1446
|
+
if (!error || typeof error !== "object") return false;
|
|
1447
|
+
const rec = error;
|
|
1448
|
+
if (rec.status === 404 || rec.statusCode === 404 || rec.code === "not_found") return true;
|
|
1449
|
+
const message = error instanceof Error ? error.message : "";
|
|
1450
|
+
return /\b404\b/.test(message);
|
|
1451
|
+
}
|
|
964
1452
|
|
|
965
1453
|
// src/store/platform.ts
|
|
966
1454
|
var cache2 = /* @__PURE__ */ new Map();
|
|
@@ -991,19 +1479,22 @@ async function initFromPlatform(opts) {
|
|
|
991
1479
|
const key = `${opts.platform}|${opts.appId}|${opts.env}`;
|
|
992
1480
|
const now = Date.now();
|
|
993
1481
|
const hit = ttl > 0 ? cache2.get(key) : void 0;
|
|
994
|
-
|
|
995
|
-
const { provider, model } =
|
|
996
|
-
if (
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1482
|
+
const config = hit && hit.expiresAt > now ? hit.value : await fetchAiConfig(opts);
|
|
1483
|
+
const { provider, model } = config;
|
|
1484
|
+
if (ttl > 0 && (!hit || hit.expiresAt <= now)) cache2.set(key, { value: config, expiresAt: now + ttl });
|
|
1485
|
+
const sourceCatalog = opts.initOptions?.catalog ?? DEFAULT_CATALOG;
|
|
1486
|
+
const catalog = Object.fromEntries(
|
|
1487
|
+
Object.entries(sourceCatalog).filter(([, spec]) => spec.provider === provider)
|
|
1488
|
+
);
|
|
1489
|
+
const defaultModel = model ?? opts.initOptions?.defaultModel;
|
|
1490
|
+
if (defaultModel) resolveModel(catalog, defaultModel);
|
|
1000
1491
|
const ai = init({
|
|
1001
1492
|
...opts.initOptions,
|
|
1002
|
-
|
|
1003
|
-
|
|
1493
|
+
catalog,
|
|
1494
|
+
resolveKey: odlaDbKeyResolver(opts.db, opts.keyResolverOptions),
|
|
1495
|
+
defaultModel
|
|
1004
1496
|
});
|
|
1005
1497
|
const value = { ai, provider, ...model ? { model } : {} };
|
|
1006
|
-
if (ttl > 0) cache2.set(key, { value, expiresAt: now + ttl });
|
|
1007
1498
|
return value;
|
|
1008
1499
|
}
|
|
1009
1500
|
|
|
@@ -1196,11 +1687,13 @@ export {
|
|
|
1196
1687
|
AGENT_SCHEMA,
|
|
1197
1688
|
ANTHROPIC_MODELS,
|
|
1198
1689
|
AuthError,
|
|
1690
|
+
CancelledError,
|
|
1199
1691
|
CapabilityError,
|
|
1200
1692
|
ConfigError,
|
|
1201
1693
|
ContextWindowError,
|
|
1202
1694
|
DEFAULT_CATALOG,
|
|
1203
1695
|
DEFAULT_SECRET_NAMES,
|
|
1696
|
+
DeadlineExceededError,
|
|
1204
1697
|
GOOGLE_MODELS,
|
|
1205
1698
|
InMemoryScope,
|
|
1206
1699
|
InvalidRequestError,
|
|
@@ -1211,6 +1704,7 @@ export {
|
|
|
1211
1704
|
ProviderError,
|
|
1212
1705
|
RateLimitError,
|
|
1213
1706
|
TaintError,
|
|
1707
|
+
ToolInputError,
|
|
1214
1708
|
addUsage,
|
|
1215
1709
|
assertSinkAcceptsTaint,
|
|
1216
1710
|
blocksOf,
|
|
@@ -1256,6 +1750,7 @@ export {
|
|
|
1256
1750
|
structuredMatch,
|
|
1257
1751
|
taint,
|
|
1258
1752
|
toOracleTool,
|
|
1753
|
+
validateJsonSchema,
|
|
1259
1754
|
validateRequest
|
|
1260
1755
|
};
|
|
1261
1756
|
//# sourceMappingURL=index.js.map
|