@drawbridge/drawbridge-utils 0.0.79 → 0.0.80
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/dist/ai.cjs +31 -4
- package/dist/ai.d.cts +35 -4
- package/dist/ai.d.ts +35 -4
- package/dist/ai.js +21 -4
- package/package.json +2 -2
package/dist/ai.cjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
1
2
|
var __defProp = Object.defineProperty;
|
|
2
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
7
|
var __export = (target, all) => {
|
|
6
8
|
for (var name in all)
|
|
@@ -14,6 +16,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
14
16
|
}
|
|
15
17
|
return to;
|
|
16
18
|
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
17
27
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
28
|
|
|
19
29
|
// lib/ai.js
|
|
@@ -251,6 +261,13 @@ var ai = {
|
|
|
251
261
|
bill: ({ db, user, model, usage, tools }) => (user == null ? void 0 : user.id) ? () => billRequest({ db, user, model, usage, tools }) : () => Promise.resolve()
|
|
252
262
|
};
|
|
253
263
|
|
|
264
|
+
// lib/fetch.js
|
|
265
|
+
var import_qs = __toESM(require("qs"), 1);
|
|
266
|
+
var isTransientError = (error) => {
|
|
267
|
+
const status = error == null ? void 0 : error.status;
|
|
268
|
+
return !status || status === 429 || status >= 500;
|
|
269
|
+
};
|
|
270
|
+
|
|
254
271
|
// lib/ai.js
|
|
255
272
|
var google_client = new import_genai.GoogleGenAI({
|
|
256
273
|
apiKey: process.env.GOOGLE_GEMINI_API_KEY
|
|
@@ -260,6 +277,16 @@ var googleCircuit = circuit({
|
|
|
260
277
|
threshold: 3,
|
|
261
278
|
timeout: 6e4
|
|
262
279
|
});
|
|
280
|
+
var withRetries = async (fn, { attempts = 3, delay = 500 } = {}) => {
|
|
281
|
+
for (let attempt = 1; ; attempt++) {
|
|
282
|
+
try {
|
|
283
|
+
return await fn();
|
|
284
|
+
} catch (error) {
|
|
285
|
+
if (attempt >= attempts || !isTransientError(error)) throw error;
|
|
286
|
+
await new Promise((resolve) => setTimeout(resolve, delay * attempt));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
};
|
|
263
290
|
var models = {
|
|
264
291
|
image: "gemini-2.5-flash-image",
|
|
265
292
|
text: "gemini-3.5-flash"
|
|
@@ -293,14 +320,14 @@ var google = {
|
|
|
293
320
|
...refs.map((ref) => ({ inlineData: { data: ref.data, mimeType: ref.mimeType } })),
|
|
294
321
|
{ text: prompt.join("\n") }
|
|
295
322
|
];
|
|
296
|
-
const response = await googleCircuit(() => google_client.models.generateContent({
|
|
323
|
+
const response = await googleCircuit(() => withRetries(() => google_client.models.generateContent({
|
|
297
324
|
model,
|
|
298
325
|
contents: { parts },
|
|
299
326
|
config: {
|
|
300
327
|
...config,
|
|
301
328
|
responseModalities: ["Image"]
|
|
302
329
|
}
|
|
303
|
-
}));
|
|
330
|
+
})));
|
|
304
331
|
const content = (_f = (_e = (_d = (_c = (_b = (_a = response == null ? void 0 : response.candidates) == null ? void 0 : _a[0]) == null ? void 0 : _b.content) == null ? void 0 : _c.parts) == null ? void 0 : _d[0]) == null ? void 0 : _e.inlineData) == null ? void 0 : _f.data;
|
|
305
332
|
if (!content) {
|
|
306
333
|
throw new Error("Could not generate from prompt. Please try again with a different prompt.");
|
|
@@ -321,14 +348,14 @@ var google = {
|
|
|
321
348
|
}) => {
|
|
322
349
|
const model = models.text;
|
|
323
350
|
const contents = parts ? { parts } : images.length ? { parts: [...images.map((image) => ({ inlineData: { data: image.data, mimeType: image.mimeType } })), { text: prompt.join("\n") }] } : prompt.join("\n");
|
|
324
|
-
const response = await googleCircuit(() => google_client.models.generateContent({
|
|
351
|
+
const response = await googleCircuit(() => withRetries(() => google_client.models.generateContent({
|
|
325
352
|
config: {
|
|
326
353
|
...config,
|
|
327
354
|
responseModalities: ["Text"]
|
|
328
355
|
},
|
|
329
356
|
contents,
|
|
330
357
|
model
|
|
331
|
-
}));
|
|
358
|
+
})));
|
|
332
359
|
const content = response == null ? void 0 : response.text;
|
|
333
360
|
if (!content) {
|
|
334
361
|
throw new Error("Could not generate from prompt. Please try again with a different prompt.");
|
package/dist/ai.d.cts
CHANGED
|
@@ -2,9 +2,11 @@ import { GoogleGenAI } from '@google/genai';
|
|
|
2
2
|
import { circuit } from './circuit.cjs';
|
|
3
3
|
import { ai } from './billing.cjs';
|
|
4
4
|
export { MARKUP } from './billing.cjs';
|
|
5
|
+
import { isTransientError } from './fetch.cjs';
|
|
5
6
|
import './transactions.cjs';
|
|
6
7
|
import '@drawbridge/drawbridge-telemetry';
|
|
7
8
|
import './usage.cjs';
|
|
9
|
+
import 'qs';
|
|
8
10
|
|
|
9
11
|
// AI request entry point. Wraps the Google GenAI client with a per-process
|
|
10
12
|
// circuit breaker, owns the per-model pricing table, and atomically debits
|
|
@@ -35,6 +37,35 @@ const googleCircuit = circuit({
|
|
|
35
37
|
timeout : 60000
|
|
36
38
|
});
|
|
37
39
|
|
|
40
|
+
// Bounded retry for the raw model call. A single transient failure — undici
|
|
41
|
+
// "fetch failed" on a resource-starved pod, a 429, a 5xx — must not lose the
|
|
42
|
+
// generation step OR count toward the breaker: three of those trip it and
|
|
43
|
+
// hard-fail every AI call for the next 60s. Runs INSIDE the googleCircuit
|
|
44
|
+
// callback so the breaker only counts calls that failed after retries were
|
|
45
|
+
// exhausted, and an OPEN circuit still fails fast without retrying.
|
|
46
|
+
// isTransientError treats status-less errors as transient, which is correct
|
|
47
|
+
// here: the wrapped fn is exactly one SDK call, and the GenAI SDK reports
|
|
48
|
+
// network-level failures as plain Errors with no `.status`.
|
|
49
|
+
const withRetries = async ( fn, { attempts = 3, delay = 500 } = {} ) => {
|
|
50
|
+
|
|
51
|
+
for( let attempt = 1; ; attempt++ ){
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
|
|
55
|
+
return await fn();
|
|
56
|
+
|
|
57
|
+
} catch ( error ) {
|
|
58
|
+
|
|
59
|
+
if( attempt >= attempts || ! isTransientError( error ) ) throw error;
|
|
60
|
+
|
|
61
|
+
await new Promise( ( resolve ) => setTimeout( resolve, delay * attempt ) );
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
};
|
|
68
|
+
|
|
38
69
|
// gemini-2.5-flash was retired by Google (generateContent → 404 "no longer
|
|
39
70
|
// available"), taking down every generation. gemini-3.5-flash is the current
|
|
40
71
|
// stable flash successor. The image model is a distinct model and still live,
|
|
@@ -89,14 +120,14 @@ const google = {
|
|
|
89
120
|
{ text : prompt.join( '\n' ) }
|
|
90
121
|
];
|
|
91
122
|
|
|
92
|
-
const response = await googleCircuit( () => google_client.models.generateContent({
|
|
123
|
+
const response = await googleCircuit( () => withRetries( () => google_client.models.generateContent({
|
|
93
124
|
model,
|
|
94
125
|
contents : { parts },
|
|
95
126
|
config : {
|
|
96
127
|
...config,
|
|
97
128
|
responseModalities : [ 'Image' ]
|
|
98
129
|
}
|
|
99
|
-
}) );
|
|
130
|
+
}) ) );
|
|
100
131
|
|
|
101
132
|
const content = response?.candidates?.[ 0 ]?.content?.parts?.[ 0 ]?.inlineData?.data;
|
|
102
133
|
|
|
@@ -143,14 +174,14 @@ const google = {
|
|
|
143
174
|
? { parts : [ ...images.map( ( image ) => ({ inlineData : { data : image.data, mimeType : image.mimeType } }) ), { text : prompt.join( '\n' ) } ] }
|
|
144
175
|
: prompt.join( '\n' );
|
|
145
176
|
|
|
146
|
-
const response = await googleCircuit( () => google_client.models.generateContent({
|
|
177
|
+
const response = await googleCircuit( () => withRetries( () => google_client.models.generateContent({
|
|
147
178
|
config : {
|
|
148
179
|
...config,
|
|
149
180
|
responseModalities : [ 'Text' ]
|
|
150
181
|
},
|
|
151
182
|
contents,
|
|
152
183
|
model
|
|
153
|
-
}) );
|
|
184
|
+
}) ) );
|
|
154
185
|
|
|
155
186
|
const content = response?.text;
|
|
156
187
|
|
package/dist/ai.d.ts
CHANGED
|
@@ -2,9 +2,11 @@ import { GoogleGenAI } from '@google/genai';
|
|
|
2
2
|
import { circuit } from './circuit.js';
|
|
3
3
|
import { ai } from './billing.js';
|
|
4
4
|
export { MARKUP } from './billing.js';
|
|
5
|
+
import { isTransientError } from './fetch.js';
|
|
5
6
|
import './transactions.js';
|
|
6
7
|
import '@drawbridge/drawbridge-telemetry';
|
|
7
8
|
import './usage.js';
|
|
9
|
+
import 'qs';
|
|
8
10
|
|
|
9
11
|
// AI request entry point. Wraps the Google GenAI client with a per-process
|
|
10
12
|
// circuit breaker, owns the per-model pricing table, and atomically debits
|
|
@@ -35,6 +37,35 @@ const googleCircuit = circuit({
|
|
|
35
37
|
timeout : 60000
|
|
36
38
|
});
|
|
37
39
|
|
|
40
|
+
// Bounded retry for the raw model call. A single transient failure — undici
|
|
41
|
+
// "fetch failed" on a resource-starved pod, a 429, a 5xx — must not lose the
|
|
42
|
+
// generation step OR count toward the breaker: three of those trip it and
|
|
43
|
+
// hard-fail every AI call for the next 60s. Runs INSIDE the googleCircuit
|
|
44
|
+
// callback so the breaker only counts calls that failed after retries were
|
|
45
|
+
// exhausted, and an OPEN circuit still fails fast without retrying.
|
|
46
|
+
// isTransientError treats status-less errors as transient, which is correct
|
|
47
|
+
// here: the wrapped fn is exactly one SDK call, and the GenAI SDK reports
|
|
48
|
+
// network-level failures as plain Errors with no `.status`.
|
|
49
|
+
const withRetries = async ( fn, { attempts = 3, delay = 500 } = {} ) => {
|
|
50
|
+
|
|
51
|
+
for( let attempt = 1; ; attempt++ ){
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
|
|
55
|
+
return await fn();
|
|
56
|
+
|
|
57
|
+
} catch ( error ) {
|
|
58
|
+
|
|
59
|
+
if( attempt >= attempts || ! isTransientError( error ) ) throw error;
|
|
60
|
+
|
|
61
|
+
await new Promise( ( resolve ) => setTimeout( resolve, delay * attempt ) );
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
};
|
|
68
|
+
|
|
38
69
|
// gemini-2.5-flash was retired by Google (generateContent → 404 "no longer
|
|
39
70
|
// available"), taking down every generation. gemini-3.5-flash is the current
|
|
40
71
|
// stable flash successor. The image model is a distinct model and still live,
|
|
@@ -89,14 +120,14 @@ const google = {
|
|
|
89
120
|
{ text : prompt.join( '\n' ) }
|
|
90
121
|
];
|
|
91
122
|
|
|
92
|
-
const response = await googleCircuit( () => google_client.models.generateContent({
|
|
123
|
+
const response = await googleCircuit( () => withRetries( () => google_client.models.generateContent({
|
|
93
124
|
model,
|
|
94
125
|
contents : { parts },
|
|
95
126
|
config : {
|
|
96
127
|
...config,
|
|
97
128
|
responseModalities : [ 'Image' ]
|
|
98
129
|
}
|
|
99
|
-
}) );
|
|
130
|
+
}) ) );
|
|
100
131
|
|
|
101
132
|
const content = response?.candidates?.[ 0 ]?.content?.parts?.[ 0 ]?.inlineData?.data;
|
|
102
133
|
|
|
@@ -143,14 +174,14 @@ const google = {
|
|
|
143
174
|
? { parts : [ ...images.map( ( image ) => ({ inlineData : { data : image.data, mimeType : image.mimeType } }) ), { text : prompt.join( '\n' ) } ] }
|
|
144
175
|
: prompt.join( '\n' );
|
|
145
176
|
|
|
146
|
-
const response = await googleCircuit( () => google_client.models.generateContent({
|
|
177
|
+
const response = await googleCircuit( () => withRetries( () => google_client.models.generateContent({
|
|
147
178
|
config : {
|
|
148
179
|
...config,
|
|
149
180
|
responseModalities : [ 'Text' ]
|
|
150
181
|
},
|
|
151
182
|
contents,
|
|
152
183
|
model
|
|
153
|
-
}) );
|
|
184
|
+
}) ) );
|
|
154
185
|
|
|
155
186
|
const content = response?.text;
|
|
156
187
|
|
package/dist/ai.js
CHANGED
|
@@ -227,6 +227,13 @@ var ai = {
|
|
|
227
227
|
bill: ({ db, user, model, usage, tools }) => (user == null ? void 0 : user.id) ? () => billRequest({ db, user, model, usage, tools }) : () => Promise.resolve()
|
|
228
228
|
};
|
|
229
229
|
|
|
230
|
+
// lib/fetch.js
|
|
231
|
+
import qs from "qs";
|
|
232
|
+
var isTransientError = (error) => {
|
|
233
|
+
const status = error == null ? void 0 : error.status;
|
|
234
|
+
return !status || status === 429 || status >= 500;
|
|
235
|
+
};
|
|
236
|
+
|
|
230
237
|
// lib/ai.js
|
|
231
238
|
var google_client = new GoogleGenAI({
|
|
232
239
|
apiKey: process.env.GOOGLE_GEMINI_API_KEY
|
|
@@ -236,6 +243,16 @@ var googleCircuit = circuit({
|
|
|
236
243
|
threshold: 3,
|
|
237
244
|
timeout: 6e4
|
|
238
245
|
});
|
|
246
|
+
var withRetries = async (fn, { attempts = 3, delay = 500 } = {}) => {
|
|
247
|
+
for (let attempt = 1; ; attempt++) {
|
|
248
|
+
try {
|
|
249
|
+
return await fn();
|
|
250
|
+
} catch (error) {
|
|
251
|
+
if (attempt >= attempts || !isTransientError(error)) throw error;
|
|
252
|
+
await new Promise((resolve) => setTimeout(resolve, delay * attempt));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
};
|
|
239
256
|
var models = {
|
|
240
257
|
image: "gemini-2.5-flash-image",
|
|
241
258
|
text: "gemini-3.5-flash"
|
|
@@ -269,14 +286,14 @@ var google = {
|
|
|
269
286
|
...refs.map((ref) => ({ inlineData: { data: ref.data, mimeType: ref.mimeType } })),
|
|
270
287
|
{ text: prompt.join("\n") }
|
|
271
288
|
];
|
|
272
|
-
const response = await googleCircuit(() => google_client.models.generateContent({
|
|
289
|
+
const response = await googleCircuit(() => withRetries(() => google_client.models.generateContent({
|
|
273
290
|
model,
|
|
274
291
|
contents: { parts },
|
|
275
292
|
config: {
|
|
276
293
|
...config,
|
|
277
294
|
responseModalities: ["Image"]
|
|
278
295
|
}
|
|
279
|
-
}));
|
|
296
|
+
})));
|
|
280
297
|
const content = (_f = (_e = (_d = (_c = (_b = (_a = response == null ? void 0 : response.candidates) == null ? void 0 : _a[0]) == null ? void 0 : _b.content) == null ? void 0 : _c.parts) == null ? void 0 : _d[0]) == null ? void 0 : _e.inlineData) == null ? void 0 : _f.data;
|
|
281
298
|
if (!content) {
|
|
282
299
|
throw new Error("Could not generate from prompt. Please try again with a different prompt.");
|
|
@@ -297,14 +314,14 @@ var google = {
|
|
|
297
314
|
}) => {
|
|
298
315
|
const model = models.text;
|
|
299
316
|
const contents = parts ? { parts } : images.length ? { parts: [...images.map((image) => ({ inlineData: { data: image.data, mimeType: image.mimeType } })), { text: prompt.join("\n") }] } : prompt.join("\n");
|
|
300
|
-
const response = await googleCircuit(() => google_client.models.generateContent({
|
|
317
|
+
const response = await googleCircuit(() => withRetries(() => google_client.models.generateContent({
|
|
301
318
|
config: {
|
|
302
319
|
...config,
|
|
303
320
|
responseModalities: ["Text"]
|
|
304
321
|
},
|
|
305
322
|
contents,
|
|
306
323
|
model
|
|
307
|
-
}));
|
|
324
|
+
})));
|
|
308
325
|
const content = response == null ? void 0 : response.text;
|
|
309
326
|
if (!content) {
|
|
310
327
|
throw new Error("Could not generate from prompt. Please try again with a different prompt.");
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"type": "module",
|
|
3
3
|
"dependencies": {
|
|
4
4
|
"@drawbridge/drawbridge-agents": "0.0.10",
|
|
5
|
-
"@drawbridge/drawbridge-telemetry": "0.0.
|
|
5
|
+
"@drawbridge/drawbridge-telemetry": "0.0.15",
|
|
6
6
|
"@google/genai": "1.30.0",
|
|
7
7
|
"axios": "1.16.0",
|
|
8
8
|
"currency-codes": "2.2.0",
|
|
@@ -164,5 +164,5 @@
|
|
|
164
164
|
"test": "node --test test/"
|
|
165
165
|
},
|
|
166
166
|
"types": "dist/index.d.ts",
|
|
167
|
-
"version": "0.0.
|
|
167
|
+
"version": "0.0.80"
|
|
168
168
|
}
|