avantgate 1.6.1 → 1.8.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/README.md +21 -59
- package/dist/agent/index.js +97 -24
- package/dist/agent/index.mjs +5 -2
- package/dist/agent/isolated-tool.d.ts +8 -3
- package/dist/agent/isolated-tool.d.ts.map +1 -1
- package/dist/agent/types.d.ts +16 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/{chunk-CO26LNFD.mjs → chunk-36ULFTM2.mjs} +8 -8
- package/dist/{chunk-4XWO3NZW.mjs → chunk-AH5XXSS4.mjs} +1 -1
- package/dist/{chunk-5VA3EKPJ.mjs → chunk-W27A25DK.mjs} +97 -25
- package/dist/chunk-Y6FXYEAI.mjs +10 -0
- package/dist/client/index.mjs +2 -0
- package/dist/control-layer.d.ts +8 -7
- package/dist/control-layer.d.ts.map +1 -1
- package/dist/finance/index.js +6 -6
- package/dist/finance/index.mjs +2 -1
- package/dist/finance/normalizer.d.ts +2 -2
- package/dist/finance/normalizer.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +558 -133
- package/dist/index.mjs +451 -106
- package/dist/input-guard.d.ts +2 -2
- package/dist/input-guard.d.ts.map +1 -1
- package/dist/pricing.d.ts +17 -16
- package/dist/pricing.d.ts.map +1 -1
- package/dist/providers/http-client.d.ts +2 -2
- package/dist/providers/http-client.d.ts.map +1 -1
- package/dist/response-validator.d.ts +2 -2
- package/dist/response-validator.d.ts.map +1 -1
- package/dist/sanitizer.d.ts +1 -1
- package/dist/sanitizer.d.ts.map +1 -1
- package/dist/secret-guard.d.ts +29 -0
- package/dist/secret-guard.d.ts.map +1 -0
- package/dist/sqlite-pricing-adapter.d.ts +87 -0
- package/dist/sqlite-pricing-adapter.d.ts.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/workflow/index.js +92 -24
- package/dist/workflow/index.mjs +3 -2
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -23,6 +33,7 @@ __export(index_exports, {
|
|
|
23
33
|
AvantGateBudgetExceededError: () => BudgetExceededError,
|
|
24
34
|
AvantGateConfigurationError: () => ConfigurationError,
|
|
25
35
|
AvantGateControlLayer: () => AvantGateControlLayer,
|
|
36
|
+
AvantGateSecretLeakBlockedError: () => SecretLeakBlockedError,
|
|
26
37
|
BudgetExceededError: () => BudgetExceededError,
|
|
27
38
|
CachedPricingAdapter: () => CachedPricingAdapter,
|
|
28
39
|
ConfigurationError: () => ConfigurationError,
|
|
@@ -33,10 +44,13 @@ __export(index_exports, {
|
|
|
33
44
|
PromptRegistry: () => PromptRegistry,
|
|
34
45
|
PromptTemplate: () => PromptTemplate,
|
|
35
46
|
SEED_MODEL_PRICES: () => SEED_MODEL_PRICES,
|
|
47
|
+
SecretLeakBlockedError: () => SecretLeakBlockedError,
|
|
48
|
+
SqlitePricingAdapter: () => SqlitePricingAdapter,
|
|
36
49
|
WorkflowAbortSignal: () => WorkflowAbortSignal,
|
|
37
50
|
WorkflowSagaRollbackError: () => WorkflowSagaRollbackError,
|
|
38
51
|
WorkflowValidationError: () => WorkflowValidationError,
|
|
39
52
|
ZenLLMControlLayer: () => AvantGateControlLayer,
|
|
53
|
+
applyOutputGuards: () => applyOutputGuards,
|
|
40
54
|
calculateCostUSD: () => calculateCostUSD,
|
|
41
55
|
createAvantGate: () => createAvantGate,
|
|
42
56
|
createHttpProviderClient: () => createHttpProviderClient,
|
|
@@ -48,6 +62,7 @@ __export(index_exports, {
|
|
|
48
62
|
resolveModelPrice: () => resolveModelPrice,
|
|
49
63
|
resolveModelPriceAsync: () => resolveModelPriceAsync,
|
|
50
64
|
sanitizePII: () => sanitizePII,
|
|
65
|
+
sanitizeSecrets: () => sanitizeSecrets,
|
|
51
66
|
validateUserInput: () => validateUserInput,
|
|
52
67
|
validateWithZod: () => validateWithZod
|
|
53
68
|
});
|
|
@@ -66,6 +81,221 @@ var BudgetExceededError = class extends Error {
|
|
|
66
81
|
this.name = "BudgetExceededError";
|
|
67
82
|
}
|
|
68
83
|
};
|
|
84
|
+
var SecretLeakBlockedError = class extends Error {
|
|
85
|
+
detections;
|
|
86
|
+
constructor(message, detections) {
|
|
87
|
+
super(message);
|
|
88
|
+
this.name = "SecretLeakBlockedError";
|
|
89
|
+
this.detections = detections;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// src/sqlite-pricing-adapter.ts
|
|
94
|
+
var import_fs = __toESM(require("fs"));
|
|
95
|
+
var import_path = __toESM(require("path"));
|
|
96
|
+
var normalizeIdentifier = (val) => {
|
|
97
|
+
return val.trim().toLowerCase().replace(":", "/");
|
|
98
|
+
};
|
|
99
|
+
var SqlitePricingAdapter = class {
|
|
100
|
+
db;
|
|
101
|
+
tableName;
|
|
102
|
+
constructor(options = {}) {
|
|
103
|
+
this.tableName = options.tableName ?? "model_pricing";
|
|
104
|
+
this.db = options.db ?? this.initDatabase(options.dbPath ?? ":memory:");
|
|
105
|
+
this.initSchema();
|
|
106
|
+
if (options.autoSeed) {
|
|
107
|
+
this.seedDefaultPrices(false);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
initDatabase = (dbPath) => {
|
|
111
|
+
if (dbPath !== ":memory:") {
|
|
112
|
+
const dir = import_path.default.dirname(import_path.default.resolve(dbPath));
|
|
113
|
+
if (!import_fs.default.existsSync(dir)) {
|
|
114
|
+
import_fs.default.mkdirSync(dir, { recursive: true });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const { DatabaseSync } = require("sqlite");
|
|
119
|
+
return new DatabaseSync(dbPath);
|
|
120
|
+
} catch (err) {
|
|
121
|
+
throw new ConfigurationError(
|
|
122
|
+
`[avantGate] Failed to initialize native SQLite at '${dbPath}'. Ensure you are running on Node.js 22+ or provide a custom 'db' instance in options. Error: ${String(err)}`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
initSchema = () => {
|
|
127
|
+
this.db.exec(`
|
|
128
|
+
CREATE TABLE IF NOT EXISTS ${this.tableName} (
|
|
129
|
+
provider TEXT NOT NULL,
|
|
130
|
+
model TEXT NOT NULL,
|
|
131
|
+
prompt_usd_per_million REAL NOT NULL,
|
|
132
|
+
completion_usd_per_million REAL NOT NULL,
|
|
133
|
+
cache_hit_usd_per_million REAL,
|
|
134
|
+
is_active INTEGER DEFAULT 1,
|
|
135
|
+
updated_at TEXT NOT NULL,
|
|
136
|
+
PRIMARY KEY (provider, model)
|
|
137
|
+
);
|
|
138
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tableName}_lookup
|
|
139
|
+
ON ${this.tableName}(provider, model, is_active);
|
|
140
|
+
`);
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Implémentation du port avantGate PricingAdapter.
|
|
144
|
+
* Résolution hiérarchique : (provider, model) -> (provider/model) -> (model).
|
|
145
|
+
*/
|
|
146
|
+
fetchPrice = (model, provider) => {
|
|
147
|
+
const normModel = normalizeIdentifier(model);
|
|
148
|
+
const normProvider = provider ? normalizeIdentifier(provider) : void 0;
|
|
149
|
+
if (normProvider) {
|
|
150
|
+
const row = this.queryActiveRow(normProvider, normModel);
|
|
151
|
+
if (row) return this.mapRowToPrice(row);
|
|
152
|
+
}
|
|
153
|
+
if (normModel.includes("/")) {
|
|
154
|
+
const firstSlashIdx = normModel.indexOf("/");
|
|
155
|
+
const extractedProvider = normModel.slice(0, firstSlashIdx);
|
|
156
|
+
const extractedModel = normModel.slice(firstSlashIdx + 1);
|
|
157
|
+
const splitRow = this.queryActiveRow(extractedProvider, extractedModel);
|
|
158
|
+
if (splitRow) return this.mapRowToPrice(splitRow);
|
|
159
|
+
}
|
|
160
|
+
const directRow = this.queryByModelOnly(normModel);
|
|
161
|
+
if (directRow) return this.mapRowToPrice(directRow);
|
|
162
|
+
return void 0;
|
|
163
|
+
};
|
|
164
|
+
queryActiveRow = (provider, model) => {
|
|
165
|
+
const stmt = this.db.prepare(`
|
|
166
|
+
SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
|
|
167
|
+
FROM ${this.tableName}
|
|
168
|
+
WHERE provider = ? AND model = ? AND is_active = 1
|
|
169
|
+
LIMIT 1
|
|
170
|
+
`);
|
|
171
|
+
return stmt.get(provider, model);
|
|
172
|
+
};
|
|
173
|
+
queryByModelOnly = (model) => {
|
|
174
|
+
const stmt = this.db.prepare(`
|
|
175
|
+
SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
|
|
176
|
+
FROM ${this.tableName}
|
|
177
|
+
WHERE model = ? AND is_active = 1
|
|
178
|
+
LIMIT 1
|
|
179
|
+
`);
|
|
180
|
+
return stmt.get(model);
|
|
181
|
+
};
|
|
182
|
+
mapRowToPrice = (row) => {
|
|
183
|
+
return {
|
|
184
|
+
promptUSDPerMillion: Number(row.prompt_usd_per_million),
|
|
185
|
+
completionUSDPerMillion: Number(row.completion_usd_per_million),
|
|
186
|
+
cacheHitUSDPerMillion: row.cache_hit_usd_per_million != null ? Number(row.cache_hit_usd_per_million) : void 0
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* Enregistre ou met à jour la tarification d'un modèle.
|
|
191
|
+
*/
|
|
192
|
+
upsertPrice = (provider, model, price, isActive = true) => {
|
|
193
|
+
const normProvider = normalizeIdentifier(provider);
|
|
194
|
+
const normModel = normalizeIdentifier(model);
|
|
195
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
196
|
+
const stmt = this.db.prepare(`
|
|
197
|
+
INSERT OR REPLACE INTO ${this.tableName}
|
|
198
|
+
(provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at)
|
|
199
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
200
|
+
`);
|
|
201
|
+
stmt.run(
|
|
202
|
+
normProvider,
|
|
203
|
+
normModel,
|
|
204
|
+
price.promptUSDPerMillion,
|
|
205
|
+
price.completionUSDPerMillion,
|
|
206
|
+
price.cacheHitUSDPerMillion ?? null,
|
|
207
|
+
isActive ? 1 : 0,
|
|
208
|
+
now
|
|
209
|
+
);
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Supprime une règle tarifaire de la base de données.
|
|
213
|
+
*/
|
|
214
|
+
deletePrice = (provider, model) => {
|
|
215
|
+
const normProvider = normalizeIdentifier(provider);
|
|
216
|
+
const normModel = normalizeIdentifier(model);
|
|
217
|
+
const stmt = this.db.prepare(`
|
|
218
|
+
DELETE FROM ${this.tableName}
|
|
219
|
+
WHERE provider = ? AND model = ?
|
|
220
|
+
`);
|
|
221
|
+
const result = stmt.run(normProvider, normModel);
|
|
222
|
+
return Boolean(result && typeof result.changes === "number" && result.changes > 0);
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* Retourne l'ensemble des règles tarifaires enregistrées.
|
|
226
|
+
*/
|
|
227
|
+
getAllPrices = () => {
|
|
228
|
+
const stmt = this.db.prepare(`
|
|
229
|
+
SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
|
|
230
|
+
FROM ${this.tableName}
|
|
231
|
+
ORDER BY provider ASC, model ASC
|
|
232
|
+
`);
|
|
233
|
+
const rows = stmt.all();
|
|
234
|
+
return rows.map((row) => ({
|
|
235
|
+
provider: row.provider,
|
|
236
|
+
model: row.model,
|
|
237
|
+
price: this.mapRowToPrice(row),
|
|
238
|
+
isActive: Boolean(row.is_active),
|
|
239
|
+
updatedAt: row.updated_at
|
|
240
|
+
}));
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Remplit la base avec le catalogue de départ SEED_MODEL_PRICES.
|
|
244
|
+
* @param force Si true, écrase les tarifs existants. Si false, insère uniquement les modèles manquants.
|
|
245
|
+
* @returns Le nombre d'enregistrements insérés.
|
|
246
|
+
*/
|
|
247
|
+
seedDefaultPrices = (force = false) => {
|
|
248
|
+
const conflictClause = force ? "INSERT OR REPLACE" : "INSERT OR IGNORE";
|
|
249
|
+
const stmt = this.db.prepare(`
|
|
250
|
+
${conflictClause} INTO ${this.tableName}
|
|
251
|
+
(provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at)
|
|
252
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
253
|
+
`);
|
|
254
|
+
let inserted = 0;
|
|
255
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
256
|
+
for (const [key, price] of Object.entries(SEED_MODEL_PRICES)) {
|
|
257
|
+
const normKey = normalizeIdentifier(key);
|
|
258
|
+
let provider = "direct";
|
|
259
|
+
let model = normKey;
|
|
260
|
+
if (normKey.includes("/")) {
|
|
261
|
+
const slashIdx = normKey.indexOf("/");
|
|
262
|
+
provider = normKey.slice(0, slashIdx);
|
|
263
|
+
model = normKey.slice(slashIdx + 1);
|
|
264
|
+
}
|
|
265
|
+
const res1 = stmt.run(
|
|
266
|
+
provider,
|
|
267
|
+
model,
|
|
268
|
+
price.promptUSDPerMillion,
|
|
269
|
+
price.completionUSDPerMillion,
|
|
270
|
+
price.cacheHitUSDPerMillion ?? null,
|
|
271
|
+
1,
|
|
272
|
+
now
|
|
273
|
+
);
|
|
274
|
+
if (res1?.changes && res1.changes > 0) inserted++;
|
|
275
|
+
if (provider !== "direct") {
|
|
276
|
+
const res2 = stmt.run(
|
|
277
|
+
"direct",
|
|
278
|
+
normKey,
|
|
279
|
+
price.promptUSDPerMillion,
|
|
280
|
+
price.completionUSDPerMillion,
|
|
281
|
+
price.cacheHitUSDPerMillion ?? null,
|
|
282
|
+
1,
|
|
283
|
+
now
|
|
284
|
+
);
|
|
285
|
+
if (res2?.changes && res2.changes > 0) inserted++;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return inserted;
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* Ferme la connexion SQLite sous-jacente si supporté.
|
|
292
|
+
*/
|
|
293
|
+
close = () => {
|
|
294
|
+
if (typeof this.db.close === "function") {
|
|
295
|
+
this.db.close();
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
};
|
|
69
299
|
|
|
70
300
|
// src/pricing.ts
|
|
71
301
|
var SEED_MODEL_PRICES = {
|
|
@@ -91,9 +321,9 @@ var SEED_MODEL_PRICES = {
|
|
|
91
321
|
// Local Ollama (Toujours $0)
|
|
92
322
|
"ollama": { promptUSDPerMillion: 0, completionUSDPerMillion: 0 }
|
|
93
323
|
};
|
|
94
|
-
|
|
324
|
+
var normalizeKey = (identifier) => {
|
|
95
325
|
return identifier.trim().toLowerCase().replace(":", "/");
|
|
96
|
-
}
|
|
326
|
+
};
|
|
97
327
|
var CachedPricingAdapter = class {
|
|
98
328
|
cache = /* @__PURE__ */ new Map();
|
|
99
329
|
ttlMs;
|
|
@@ -102,7 +332,7 @@ var CachedPricingAdapter = class {
|
|
|
102
332
|
this.delegate = delegate;
|
|
103
333
|
this.ttlMs = ttlMs;
|
|
104
334
|
}
|
|
105
|
-
async
|
|
335
|
+
fetchPrice = async (model, provider) => {
|
|
106
336
|
const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
|
|
107
337
|
const cached = this.cache.get(key);
|
|
108
338
|
if (cached && Date.now() < cached.expiresAt) {
|
|
@@ -120,72 +350,76 @@ var CachedPricingAdapter = class {
|
|
|
120
350
|
}
|
|
121
351
|
}
|
|
122
352
|
return void 0;
|
|
123
|
-
}
|
|
124
|
-
peek(model, provider) {
|
|
353
|
+
};
|
|
354
|
+
peek = (model, provider) => {
|
|
125
355
|
const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
356
|
+
const cached = this.cache.get(key);
|
|
357
|
+
if (cached && Date.now() < cached.expiresAt) {
|
|
358
|
+
return cached.price;
|
|
359
|
+
}
|
|
360
|
+
return void 0;
|
|
361
|
+
};
|
|
362
|
+
clearCache = () => {
|
|
129
363
|
this.cache.clear();
|
|
130
|
-
}
|
|
131
|
-
invalidate(model, provider) {
|
|
364
|
+
};
|
|
365
|
+
invalidate = (model, provider) => {
|
|
132
366
|
if (!model) {
|
|
133
367
|
this.clearCache();
|
|
134
368
|
return;
|
|
135
369
|
}
|
|
136
370
|
const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
|
|
137
371
|
this.cache.delete(key);
|
|
138
|
-
}
|
|
372
|
+
};
|
|
139
373
|
};
|
|
140
|
-
var PricingRegistry = class {
|
|
374
|
+
var PricingRegistry = class _PricingRegistry {
|
|
141
375
|
static prices = /* @__PURE__ */ new Map();
|
|
142
376
|
static adapter;
|
|
143
|
-
static registerPrice(identifier, price) {
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
static registerDistributorPrices(distributor, priceMap) {
|
|
377
|
+
static registerPrice = (identifier, price) => {
|
|
378
|
+
_PricingRegistry.prices.set(normalizeKey(identifier), price);
|
|
379
|
+
};
|
|
380
|
+
static registerDistributorPrices = (distributor, priceMap) => {
|
|
147
381
|
for (const [model, price] of Object.entries(priceMap)) {
|
|
148
|
-
|
|
382
|
+
_PricingRegistry.prices.set(normalizeKey(`${distributor}/${model}`), price);
|
|
149
383
|
}
|
|
150
|
-
}
|
|
151
|
-
static registerPrices(prices) {
|
|
384
|
+
};
|
|
385
|
+
static registerPrices = (prices) => {
|
|
152
386
|
for (const [key, price] of Object.entries(prices)) {
|
|
153
|
-
|
|
387
|
+
_PricingRegistry.prices.set(normalizeKey(key), price);
|
|
154
388
|
}
|
|
155
|
-
}
|
|
156
|
-
static getPrice(model, provider) {
|
|
389
|
+
};
|
|
390
|
+
static getPrice = (model, provider) => {
|
|
157
391
|
if (provider) {
|
|
158
392
|
const distributorKey = normalizeKey(`${provider}/${model}`);
|
|
159
|
-
const directDistributorPrice =
|
|
393
|
+
const directDistributorPrice = _PricingRegistry.prices.get(distributorKey);
|
|
160
394
|
if (directDistributorPrice) {
|
|
161
395
|
return directDistributorPrice;
|
|
162
396
|
}
|
|
163
397
|
}
|
|
164
|
-
return
|
|
165
|
-
}
|
|
166
|
-
static setAdapter(adapter) {
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
static getAdapter() {
|
|
170
|
-
return
|
|
171
|
-
}
|
|
172
|
-
static clear() {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
398
|
+
return _PricingRegistry.prices.get(normalizeKey(model));
|
|
399
|
+
};
|
|
400
|
+
static setAdapter = (adapter) => {
|
|
401
|
+
_PricingRegistry.adapter = adapter;
|
|
402
|
+
};
|
|
403
|
+
static getAdapter = () => {
|
|
404
|
+
return _PricingRegistry.adapter;
|
|
405
|
+
};
|
|
406
|
+
static clear = () => {
|
|
407
|
+
_PricingRegistry.prices.clear();
|
|
408
|
+
_PricingRegistry.adapter = void 0;
|
|
409
|
+
};
|
|
176
410
|
/**
|
|
177
411
|
* Réinitialise le registre en chargeant le catalogue d'exemple SEED_MODEL_PRICES.
|
|
178
412
|
*/
|
|
179
|
-
static loadSeedPrices() {
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
static getAllPrices() {
|
|
413
|
+
static loadSeedPrices = () => {
|
|
414
|
+
_PricingRegistry.registerPrices(SEED_MODEL_PRICES);
|
|
415
|
+
};
|
|
416
|
+
static getAllPrices = () => {
|
|
183
417
|
const out = {};
|
|
184
|
-
for (const [k, v] of
|
|
418
|
+
for (const [k, v] of _PricingRegistry.prices.entries()) {
|
|
185
419
|
out[k] = v;
|
|
186
420
|
}
|
|
187
421
|
return out;
|
|
188
|
-
}
|
|
422
|
+
};
|
|
189
423
|
};
|
|
190
424
|
var DEFAULT_MODEL_PRICES = new Proxy({}, {
|
|
191
425
|
get(_target, prop) {
|
|
@@ -205,7 +439,7 @@ var DEFAULT_MODEL_PRICES = new Proxy({}, {
|
|
|
205
439
|
return void 0;
|
|
206
440
|
}
|
|
207
441
|
});
|
|
208
|
-
|
|
442
|
+
var resolveModelPrice = (model, options) => {
|
|
209
443
|
const opts = typeof options === "string" ? { provider: options } : options ?? {};
|
|
210
444
|
if (opts.providerPricing) {
|
|
211
445
|
return opts.providerPricing;
|
|
@@ -236,8 +470,8 @@ function resolveModelPrice(model, options) {
|
|
|
236
470
|
return { promptUSDPerMillion: 0, completionUSDPerMillion: 0 };
|
|
237
471
|
}
|
|
238
472
|
return void 0;
|
|
239
|
-
}
|
|
240
|
-
async
|
|
473
|
+
};
|
|
474
|
+
var resolveModelPriceAsync = async (model, options) => {
|
|
241
475
|
const opts = typeof options === "string" ? { provider: options } : options ?? {};
|
|
242
476
|
const activeAdapter = opts.adapter ?? PricingRegistry.getAdapter();
|
|
243
477
|
if (activeAdapter) {
|
|
@@ -250,8 +484,8 @@ async function resolveModelPriceAsync(model, options) {
|
|
|
250
484
|
}
|
|
251
485
|
}
|
|
252
486
|
return resolveModelPrice(model, options);
|
|
253
|
-
}
|
|
254
|
-
|
|
487
|
+
};
|
|
488
|
+
var calculateCostUSD = (model, promptTokens, completionTokens, cacheHitTokens = 0, options) => {
|
|
255
489
|
const pricing = resolveModelPrice(model, options);
|
|
256
490
|
if (!pricing) {
|
|
257
491
|
return 0;
|
|
@@ -260,7 +494,7 @@ function calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens
|
|
|
260
494
|
const completionCost = completionTokens / 1e6 * pricing.completionUSDPerMillion;
|
|
261
495
|
const cacheDiscount = cacheHitTokens && pricing.cacheHitUSDPerMillion ? cacheHitTokens / 1e6 * (pricing.promptUSDPerMillion - pricing.cacheHitUSDPerMillion) : 0;
|
|
262
496
|
return Math.max(0, promptCost + completionCost - cacheDiscount);
|
|
263
|
-
}
|
|
497
|
+
};
|
|
264
498
|
|
|
265
499
|
// src/sanitizer.ts
|
|
266
500
|
var EMAIL_REGEX = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b/g;
|
|
@@ -270,7 +504,7 @@ var SPI_LABELLED_REGEX = /(?:(?:num[ée]ro\s+fiscal|spi|n[°o]\s*fiscal|d[ée]cl
|
|
|
270
504
|
var SPI_FORMATTED_REGEX = /\b[0-3]\d(?:\s+\d{2}){5}\s+\d\b/g;
|
|
271
505
|
var IBAN_REGEX = /\b[A-Z]{2}\s*[0-9]{2}(?:[\s\r\n.-]*[A-Z0-9]){11,30}\b/g;
|
|
272
506
|
var BIC_LABELLED_REGEX = /(?:(?:bic|swift)\s*[:=]?\s*)\b([A-Z]{4}[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?)\b/gi;
|
|
273
|
-
|
|
507
|
+
var sanitizePII = (input) => {
|
|
274
508
|
let count = 0;
|
|
275
509
|
let result = input;
|
|
276
510
|
result = result.replace(EMAIL_REGEX, () => {
|
|
@@ -313,7 +547,7 @@ function sanitizePII(input) {
|
|
|
313
547
|
return match;
|
|
314
548
|
});
|
|
315
549
|
return { text: result, maskedCount: count };
|
|
316
|
-
}
|
|
550
|
+
};
|
|
317
551
|
|
|
318
552
|
// src/input-guard.ts
|
|
319
553
|
var INJECTION_PATTERNS = [
|
|
@@ -324,7 +558,7 @@ var INJECTION_PATTERNS = [
|
|
|
324
558
|
/output\s+(?:your\s+)?(?:system\s+prompt|initial\s+instructions)/i,
|
|
325
559
|
/repeat\s+(?:the\s+words\s+above|everything\s+above)/i
|
|
326
560
|
];
|
|
327
|
-
|
|
561
|
+
var validateUserInput = (input, options) => {
|
|
328
562
|
const maxLength = options?.maxLength ?? 5e4;
|
|
329
563
|
if (input.length > maxLength) {
|
|
330
564
|
return {
|
|
@@ -343,7 +577,7 @@ function validateUserInput(input, options) {
|
|
|
343
577
|
}
|
|
344
578
|
}
|
|
345
579
|
return { valid: true };
|
|
346
|
-
}
|
|
580
|
+
};
|
|
347
581
|
|
|
348
582
|
// src/finance/strategies/french-pcg.strategy.ts
|
|
349
583
|
var FrenchPCGStrategy = class {
|
|
@@ -799,17 +1033,17 @@ var AccountingFactory = class {
|
|
|
799
1033
|
};
|
|
800
1034
|
|
|
801
1035
|
// src/finance/normalizer.ts
|
|
802
|
-
|
|
1036
|
+
var cleanFinancialJSON = (rawText, options) => {
|
|
803
1037
|
if (!rawText) {
|
|
804
1038
|
return "";
|
|
805
1039
|
}
|
|
806
1040
|
const jsonBlock = extractAndCleanJSON(rawText);
|
|
807
1041
|
const strategy = options?.jurisdiction ? AccountingFactory.getStrategy(options.jurisdiction) : options?.autoDetect !== false ? AccountingFactory.detectStrategy(rawText) : AccountingFactory.getStrategy("FR");
|
|
808
1042
|
return strategy.cleanJSON(jsonBlock);
|
|
809
|
-
}
|
|
1043
|
+
};
|
|
810
1044
|
|
|
811
1045
|
// src/response-validator.ts
|
|
812
|
-
|
|
1046
|
+
var extractAndCleanJSON = (rawText) => {
|
|
813
1047
|
let cleaned = rawText.trim();
|
|
814
1048
|
cleaned = cleaned.replace(/<think>[\s\S]*?<\/think>/gi, "").trim();
|
|
815
1049
|
const codeBlockMatch = cleaned.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
@@ -833,8 +1067,8 @@ function extractAndCleanJSON(rawText) {
|
|
|
833
1067
|
cleaned = cleaned.slice(startIndex, endIndex + 1);
|
|
834
1068
|
}
|
|
835
1069
|
return cleaned;
|
|
836
|
-
}
|
|
837
|
-
|
|
1070
|
+
};
|
|
1071
|
+
var validateWithZod = (rawText, schema, options) => {
|
|
838
1072
|
let jsonString = extractAndCleanJSON(rawText);
|
|
839
1073
|
if (options?.normalizer) {
|
|
840
1074
|
jsonString = options.normalizer(jsonString);
|
|
@@ -849,7 +1083,7 @@ function validateWithZod(rawText, schema, options) {
|
|
|
849
1083
|
parsed = JSON.parse(sanitized);
|
|
850
1084
|
}
|
|
851
1085
|
return schema.parse(parsed);
|
|
852
|
-
}
|
|
1086
|
+
};
|
|
853
1087
|
|
|
854
1088
|
// src/providers/http-client.ts
|
|
855
1089
|
var DEFAULT_BASE_URLS = {
|
|
@@ -870,14 +1104,14 @@ var HttpProviderClient = class {
|
|
|
870
1104
|
this.apiKey = config.apiKey;
|
|
871
1105
|
this.baseUrl = config.baseUrl || DEFAULT_BASE_URLS[config.provider] || "https://api.openai.com/v1";
|
|
872
1106
|
}
|
|
873
|
-
resolveEndpoint() {
|
|
1107
|
+
resolveEndpoint = () => {
|
|
874
1108
|
const trimmed = this.baseUrl.replace(/\/+$/, "");
|
|
875
1109
|
if (trimmed.endsWith("/chat/completions")) {
|
|
876
1110
|
return trimmed;
|
|
877
1111
|
}
|
|
878
1112
|
return `${trimmed}/chat/completions`;
|
|
879
|
-
}
|
|
880
|
-
buildHeaders() {
|
|
1113
|
+
};
|
|
1114
|
+
buildHeaders = () => {
|
|
881
1115
|
const headers = {
|
|
882
1116
|
"Content-Type": "application/json"
|
|
883
1117
|
};
|
|
@@ -889,8 +1123,8 @@ var HttpProviderClient = class {
|
|
|
889
1123
|
headers["X-Title"] = "AvantGate";
|
|
890
1124
|
}
|
|
891
1125
|
return headers;
|
|
892
|
-
}
|
|
893
|
-
buildPayload(options, model) {
|
|
1126
|
+
};
|
|
1127
|
+
buildPayload = (options, model) => {
|
|
894
1128
|
return {
|
|
895
1129
|
model,
|
|
896
1130
|
messages: options.messages.map((m) => ({
|
|
@@ -900,8 +1134,8 @@ var HttpProviderClient = class {
|
|
|
900
1134
|
temperature: options.temperature ?? 0.2,
|
|
901
1135
|
...options.responseFormat ? { response_format: options.responseFormat } : {}
|
|
902
1136
|
};
|
|
903
|
-
}
|
|
904
|
-
extractUsage(usageData) {
|
|
1137
|
+
};
|
|
1138
|
+
extractUsage = (usageData) => {
|
|
905
1139
|
if (!usageData) {
|
|
906
1140
|
return void 0;
|
|
907
1141
|
}
|
|
@@ -911,8 +1145,8 @@ var HttpProviderClient = class {
|
|
|
911
1145
|
totalTokens: usageData.total_tokens,
|
|
912
1146
|
promptCacheHitTokens: usageData.prompt_tokens_details?.cached_tokens
|
|
913
1147
|
};
|
|
914
|
-
}
|
|
915
|
-
async
|
|
1148
|
+
};
|
|
1149
|
+
complete = async (options) => {
|
|
916
1150
|
const endpoint = this.resolveEndpoint();
|
|
917
1151
|
const model = options.model ?? "default";
|
|
918
1152
|
const headers = this.buildHeaders();
|
|
@@ -932,11 +1166,114 @@ var HttpProviderClient = class {
|
|
|
932
1166
|
const text = data.choices?.[0]?.message?.content ?? "";
|
|
933
1167
|
const usage = this.extractUsage(data.usage);
|
|
934
1168
|
return { text, usage };
|
|
935
|
-
}
|
|
1169
|
+
};
|
|
936
1170
|
};
|
|
937
|
-
|
|
1171
|
+
var createHttpProviderClient = (config) => {
|
|
938
1172
|
return new HttpProviderClient(config);
|
|
939
|
-
}
|
|
1173
|
+
};
|
|
1174
|
+
|
|
1175
|
+
// src/secret-guard.ts
|
|
1176
|
+
var SECRET_PATTERNS = [
|
|
1177
|
+
// 1. Clés privées (RSA, OpenSSH, EC, DSA)
|
|
1178
|
+
{
|
|
1179
|
+
type: "PRIVATE_KEY",
|
|
1180
|
+
regex: /-----BEGIN (?:[A-Z0-9_-]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z0-9_-]+ )?PRIVATE KEY-----/g,
|
|
1181
|
+
mask: "[REDACTED_PRIVATE_KEY]"
|
|
1182
|
+
},
|
|
1183
|
+
// 2. Clés Anthropic Claude (avant OpenAI pour éviter collision sk-)
|
|
1184
|
+
{
|
|
1185
|
+
type: "ANTHROPIC_API_KEY",
|
|
1186
|
+
regex: /\bsk-ant-(?:api\d{2}-)?[A-Za-z0-9_-]{20,}\b/g,
|
|
1187
|
+
mask: "[REDACTED_ANTHROPIC_KEY]"
|
|
1188
|
+
},
|
|
1189
|
+
// 3. Clés OpenAI (standard, projet, admin) - exclut explicitement sk-ant-
|
|
1190
|
+
{
|
|
1191
|
+
type: "OPENAI_API_KEY",
|
|
1192
|
+
regex: /\bsk-(?!ant-)(?:proj-|admin-|org-)?[A-Za-z0-9_-]{20,}\b/g,
|
|
1193
|
+
mask: "[REDACTED_OPENAI_KEY]"
|
|
1194
|
+
},
|
|
1195
|
+
// 4. Clés AWS Access Key ID
|
|
1196
|
+
{
|
|
1197
|
+
type: "AWS_ACCESS_KEY",
|
|
1198
|
+
regex: /\b(?:AKIA|ABIA|ACCA|ASIA)[0-9A-Z]{16}\b/g,
|
|
1199
|
+
mask: "[REDACTED_AWS_KEY]"
|
|
1200
|
+
},
|
|
1201
|
+
// 5. Tokens GitHub (Personal, OAuth, User, Server)
|
|
1202
|
+
{
|
|
1203
|
+
type: "GITHUB_TOKEN",
|
|
1204
|
+
regex: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{36,255}\b/g,
|
|
1205
|
+
mask: "[REDACTED_GITHUB_TOKEN]"
|
|
1206
|
+
},
|
|
1207
|
+
// 6. JSON Web Tokens (JWT)
|
|
1208
|
+
{
|
|
1209
|
+
type: "JWT_TOKEN",
|
|
1210
|
+
regex: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]{10,}\.[A-Za-z0-9._-]{10,}\b/g,
|
|
1211
|
+
mask: "[REDACTED_JWT]"
|
|
1212
|
+
},
|
|
1213
|
+
// 7. Tokens Slack
|
|
1214
|
+
{
|
|
1215
|
+
type: "SLACK_TOKEN",
|
|
1216
|
+
regex: /\bxox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,}\b/g,
|
|
1217
|
+
mask: "[REDACTED_SLACK_TOKEN]"
|
|
1218
|
+
},
|
|
1219
|
+
// 8. Mots de passe dans URL de connexion (Postgres, Mongo, Redis, MySQL)
|
|
1220
|
+
{
|
|
1221
|
+
type: "DATABASE_CONNECTION_SECRET",
|
|
1222
|
+
regex: /(?:postgres|postgresql|mysql|mongodb(?:\+srv)?|redis|amqp):\/\/[^:\s\/@]+:([^@\s]+)@/gi,
|
|
1223
|
+
mask: "[REDACTED_DB_SECRET]"
|
|
1224
|
+
}
|
|
1225
|
+
];
|
|
1226
|
+
var sanitizeSecrets = (input) => {
|
|
1227
|
+
let text = input;
|
|
1228
|
+
const detections = [];
|
|
1229
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
1230
|
+
let matchCount = 0;
|
|
1231
|
+
if (pattern.type === "DATABASE_CONNECTION_SECRET") {
|
|
1232
|
+
text = text.replace(pattern.regex, (fullMatch, password) => {
|
|
1233
|
+
matchCount++;
|
|
1234
|
+
return fullMatch.replace(password, pattern.mask);
|
|
1235
|
+
});
|
|
1236
|
+
} else {
|
|
1237
|
+
text = text.replace(pattern.regex, () => {
|
|
1238
|
+
matchCount++;
|
|
1239
|
+
return pattern.mask;
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
if (matchCount > 0) {
|
|
1243
|
+
detections.push({ type: pattern.type, matchedCount: matchCount });
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
return {
|
|
1247
|
+
text,
|
|
1248
|
+
hasSecrets: detections.length > 0,
|
|
1249
|
+
detections
|
|
1250
|
+
};
|
|
1251
|
+
};
|
|
1252
|
+
var applyOutputGuards = (rawOutput, options) => {
|
|
1253
|
+
let currentText = rawOutput;
|
|
1254
|
+
let piiCount = 0;
|
|
1255
|
+
if (options?.maskPII) {
|
|
1256
|
+
const piiResult = sanitizePII(currentText);
|
|
1257
|
+
currentText = piiResult.text;
|
|
1258
|
+
piiCount = piiResult.maskedCount;
|
|
1259
|
+
}
|
|
1260
|
+
const secretResult = sanitizeSecrets(currentText);
|
|
1261
|
+
currentText = secretResult.text;
|
|
1262
|
+
if (secretResult.hasSecrets && options?.secretLeakAction === "BLOCK") {
|
|
1263
|
+
const types = secretResult.detections.map((d) => d.type).join(", ");
|
|
1264
|
+
throw new SecretLeakBlockedError(
|
|
1265
|
+
`[AvantGate Output Guard] Response blocked: detected potential secret leak (${types})`,
|
|
1266
|
+
secretResult.detections
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
return {
|
|
1270
|
+
text: currentText,
|
|
1271
|
+
sanitized: piiCount > 0 || secretResult.hasSecrets,
|
|
1272
|
+
secretDetected: secretResult.hasSecrets,
|
|
1273
|
+
piiMaskedCount: piiCount,
|
|
1274
|
+
detections: secretResult.detections
|
|
1275
|
+
};
|
|
1276
|
+
};
|
|
940
1277
|
|
|
941
1278
|
// src/control-layer.ts
|
|
942
1279
|
var AvantGateControlLayer = class {
|
|
@@ -956,7 +1293,7 @@ var AvantGateControlLayer = class {
|
|
|
956
1293
|
);
|
|
957
1294
|
}
|
|
958
1295
|
}
|
|
959
|
-
resolveProviderConfig(provider) {
|
|
1296
|
+
resolveProviderConfig = (provider) => {
|
|
960
1297
|
if (!provider) return void 0;
|
|
961
1298
|
if (provider.client) return provider;
|
|
962
1299
|
if (provider.apiKey || provider.baseUrl || provider.provider === "ollama") {
|
|
@@ -966,8 +1303,8 @@ var AvantGateControlLayer = class {
|
|
|
966
1303
|
};
|
|
967
1304
|
}
|
|
968
1305
|
return provider;
|
|
969
|
-
}
|
|
970
|
-
findProviderConfig(provider, model) {
|
|
1306
|
+
};
|
|
1307
|
+
findProviderConfig = (provider, model) => {
|
|
971
1308
|
const list = [this.config.primary, this.config.fallback, this.config.emergencyFallback].filter(
|
|
972
1309
|
(p) => Boolean(p)
|
|
973
1310
|
);
|
|
@@ -980,8 +1317,8 @@ var AvantGateControlLayer = class {
|
|
|
980
1317
|
if (matchModel) return matchModel;
|
|
981
1318
|
}
|
|
982
1319
|
return this.config.primary;
|
|
983
|
-
}
|
|
984
|
-
calculateCost(model, promptTokens, completionTokens, cacheHitTokens = 0, provider) {
|
|
1320
|
+
};
|
|
1321
|
+
calculateCost = (model, promptTokens, completionTokens, cacheHitTokens = 0, provider) => {
|
|
985
1322
|
const providerCfg = this.findProviderConfig(provider, model);
|
|
986
1323
|
return calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens, {
|
|
987
1324
|
provider: provider ?? providerCfg?.provider,
|
|
@@ -989,8 +1326,8 @@ var AvantGateControlLayer = class {
|
|
|
989
1326
|
customPricing: this.config.customPricing,
|
|
990
1327
|
adapter: this.cachedPricingAdapter
|
|
991
1328
|
});
|
|
992
|
-
}
|
|
993
|
-
applySecurityGuards(userQuery) {
|
|
1329
|
+
};
|
|
1330
|
+
applySecurityGuards = (userQuery) => {
|
|
994
1331
|
const guard = validateUserInput(userQuery, {
|
|
995
1332
|
detectInjection: this.config.security?.detectPromptInjection,
|
|
996
1333
|
maxLength: this.config.security?.maxInputLength
|
|
@@ -1002,8 +1339,21 @@ var AvantGateControlLayer = class {
|
|
|
1002
1339
|
return sanitizePII(userQuery).text;
|
|
1003
1340
|
}
|
|
1004
1341
|
return userQuery;
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1342
|
+
};
|
|
1343
|
+
applyOutputSecurityGuards = (rawOutput) => {
|
|
1344
|
+
const isDlpEnabled = this.config.security?.outputDLP ?? this.config.security?.maskPII ?? false;
|
|
1345
|
+
const shouldCheckSecrets = this.config.security?.blockSecretLeaks ?? true;
|
|
1346
|
+
if (!isDlpEnabled && !shouldCheckSecrets) {
|
|
1347
|
+
return rawOutput;
|
|
1348
|
+
}
|
|
1349
|
+
const guardResult = applyOutputGuards(rawOutput, {
|
|
1350
|
+
maskPII: isDlpEnabled,
|
|
1351
|
+
blockSecretLeaks: shouldCheckSecrets,
|
|
1352
|
+
secretLeakAction: this.config.security?.secretLeakAction ?? "REDACT"
|
|
1353
|
+
});
|
|
1354
|
+
return guardResult.text;
|
|
1355
|
+
};
|
|
1356
|
+
checkPreflightBudget = async (estimatedPromptTokens, targetModel, provider) => {
|
|
1007
1357
|
if (this.config.maxTokenBudget !== void 0 && estimatedPromptTokens > this.config.maxTokenBudget) {
|
|
1008
1358
|
throw new BudgetExceededError(
|
|
1009
1359
|
`[AvantGate Budget Guard] Pre-flight token budget exceeded: estimated prompt (${estimatedPromptTokens} tokens) exceeds maxTokenBudget (${this.config.maxTokenBudget}).`
|
|
@@ -1012,7 +1362,7 @@ var AvantGateControlLayer = class {
|
|
|
1012
1362
|
if (this.config.maxCostUSD !== void 0) {
|
|
1013
1363
|
const isLocalFree = provider === "ollama" || targetModel.toLowerCase().includes("ollama");
|
|
1014
1364
|
const providerCfg = this.findProviderConfig(provider, targetModel);
|
|
1015
|
-
const price =
|
|
1365
|
+
const price = await resolveModelPriceAsync(targetModel, {
|
|
1016
1366
|
provider: provider ?? providerCfg?.provider,
|
|
1017
1367
|
providerPricing: providerCfg?.pricing,
|
|
1018
1368
|
customPricing: this.config.customPricing,
|
|
@@ -1030,8 +1380,8 @@ var AvantGateControlLayer = class {
|
|
|
1030
1380
|
);
|
|
1031
1381
|
}
|
|
1032
1382
|
}
|
|
1033
|
-
}
|
|
1034
|
-
checkPostExecutionBudget(tokensTotal, costUSD) {
|
|
1383
|
+
};
|
|
1384
|
+
checkPostExecutionBudget = (tokensTotal, costUSD) => {
|
|
1035
1385
|
if (this.config.maxTokenBudget !== void 0 && tokensTotal > this.config.maxTokenBudget) {
|
|
1036
1386
|
throw new BudgetExceededError(
|
|
1037
1387
|
`[AvantGate Budget Guard] Execution total tokens (${tokensTotal}) exceeded maxTokenBudget (${this.config.maxTokenBudget}).`
|
|
@@ -1042,16 +1392,16 @@ var AvantGateControlLayer = class {
|
|
|
1042
1392
|
`[AvantGate Budget Guard] Execution cost ($${costUSD.toFixed(6)}) exceeded maxCostUSD ($${this.config.maxCostUSD}).`
|
|
1043
1393
|
);
|
|
1044
1394
|
}
|
|
1045
|
-
}
|
|
1046
|
-
buildMessages(systemPrompt, query) {
|
|
1395
|
+
};
|
|
1396
|
+
buildMessages = (systemPrompt, query) => {
|
|
1047
1397
|
const messages = [];
|
|
1048
1398
|
if (systemPrompt) {
|
|
1049
1399
|
messages.push({ role: "system", content: systemPrompt });
|
|
1050
1400
|
}
|
|
1051
1401
|
messages.push({ role: "user", content: query });
|
|
1052
1402
|
return messages;
|
|
1053
|
-
}
|
|
1054
|
-
resolveTokens(rawUsage, query, text) {
|
|
1403
|
+
};
|
|
1404
|
+
resolveTokens = (rawUsage, query, text) => {
|
|
1055
1405
|
const promptTokens = rawUsage?.promptTokens ?? Math.ceil(query.length / 4);
|
|
1056
1406
|
const completionTokens = rawUsage?.completionTokens ?? Math.ceil(text.length / 4);
|
|
1057
1407
|
const totalTokens = rawUsage?.totalTokens ?? promptTokens + completionTokens;
|
|
@@ -1061,15 +1411,15 @@ var AvantGateControlLayer = class {
|
|
|
1061
1411
|
totalTokens,
|
|
1062
1412
|
promptCacheHitTokens: rawUsage?.promptCacheHitTokens
|
|
1063
1413
|
};
|
|
1064
|
-
}
|
|
1065
|
-
getProviderChain() {
|
|
1414
|
+
};
|
|
1415
|
+
getProviderChain = () => {
|
|
1066
1416
|
return [
|
|
1067
1417
|
this.config.primary,
|
|
1068
1418
|
this.config.fallback,
|
|
1069
1419
|
this.config.emergencyFallback
|
|
1070
1420
|
].filter((provider) => Boolean(provider?.client));
|
|
1071
|
-
}
|
|
1072
|
-
executeSimulation(query, systemPrompt) {
|
|
1421
|
+
};
|
|
1422
|
+
executeSimulation = (query, systemPrompt) => {
|
|
1073
1423
|
const promptTokens = Math.ceil(((systemPrompt?.length ?? 0) + query.length) / 4);
|
|
1074
1424
|
const completionTokens = 50;
|
|
1075
1425
|
const cost = this.calculateCost(this.config.primary.model, promptTokens, completionTokens);
|
|
@@ -1085,8 +1435,8 @@ var AvantGateControlLayer = class {
|
|
|
1085
1435
|
failoverOccurred: false,
|
|
1086
1436
|
attempts: 1
|
|
1087
1437
|
};
|
|
1088
|
-
}
|
|
1089
|
-
async
|
|
1438
|
+
};
|
|
1439
|
+
executeOverrideProvider = async (provider, messages, query, temperature) => {
|
|
1090
1440
|
const response = await provider.complete({
|
|
1091
1441
|
model: this.config.primary.model,
|
|
1092
1442
|
messages,
|
|
@@ -1100,8 +1450,8 @@ var AvantGateControlLayer = class {
|
|
|
1100
1450
|
failoverOccurred: false,
|
|
1101
1451
|
attempts: 1
|
|
1102
1452
|
};
|
|
1103
|
-
}
|
|
1104
|
-
async
|
|
1453
|
+
};
|
|
1454
|
+
executeProviderPipeline = async (messages, query, temperature, modelOverride) => {
|
|
1105
1455
|
const chain = this.getProviderChain();
|
|
1106
1456
|
let lastError;
|
|
1107
1457
|
for (let index = 0; index < chain.length; index++) {
|
|
@@ -1126,8 +1476,9 @@ var AvantGateControlLayer = class {
|
|
|
1126
1476
|
}
|
|
1127
1477
|
}
|
|
1128
1478
|
throw lastError;
|
|
1129
|
-
}
|
|
1130
|
-
assembleResult(output) {
|
|
1479
|
+
};
|
|
1480
|
+
assembleResult = (output) => {
|
|
1481
|
+
const sanitizedText = this.applyOutputSecurityGuards(output.responseText);
|
|
1131
1482
|
const costUSD = this.calculateCost(
|
|
1132
1483
|
output.modelUsed,
|
|
1133
1484
|
output.usage.promptTokens,
|
|
@@ -1135,7 +1486,7 @@ var AvantGateControlLayer = class {
|
|
|
1135
1486
|
output.usage.promptCacheHitTokens ?? 0
|
|
1136
1487
|
);
|
|
1137
1488
|
return {
|
|
1138
|
-
text:
|
|
1489
|
+
text: sanitizedText,
|
|
1139
1490
|
tokens: {
|
|
1140
1491
|
prompt: output.usage.promptTokens,
|
|
1141
1492
|
completion: output.usage.completionTokens,
|
|
@@ -1146,8 +1497,8 @@ var AvantGateControlLayer = class {
|
|
|
1146
1497
|
failoverOccurred: output.failoverOccurred,
|
|
1147
1498
|
attempts: output.attempts
|
|
1148
1499
|
};
|
|
1149
|
-
}
|
|
1150
|
-
async
|
|
1500
|
+
};
|
|
1501
|
+
notifyAuditSink = async (result) => {
|
|
1151
1502
|
if (!this.config.auditSink) {
|
|
1152
1503
|
return;
|
|
1153
1504
|
}
|
|
@@ -1159,16 +1510,16 @@ var AvantGateControlLayer = class {
|
|
|
1159
1510
|
failoverOccurred: result.failoverOccurred,
|
|
1160
1511
|
attempts: result.attempts
|
|
1161
1512
|
});
|
|
1162
|
-
}
|
|
1513
|
+
};
|
|
1163
1514
|
/**
|
|
1164
1515
|
* Exécute une requête avec garde d'entrée, masquage PII, garde pré-vol et calcul des coûts.
|
|
1165
1516
|
*/
|
|
1166
|
-
async
|
|
1517
|
+
execute = async (options) => {
|
|
1167
1518
|
const sanitizedQuery = this.applySecurityGuards(options.userQuery);
|
|
1168
1519
|
const messages = this.buildMessages(options.systemPrompt, sanitizedQuery);
|
|
1169
1520
|
const promptLength = (options.systemPrompt?.length ?? 0) + sanitizedQuery.length;
|
|
1170
1521
|
const estimatedPromptTokens = Math.ceil(promptLength / 4);
|
|
1171
|
-
this.checkPreflightBudget(estimatedPromptTokens, this.config.primary.model, this.config.primary.provider);
|
|
1522
|
+
await this.checkPreflightBudget(estimatedPromptTokens, this.config.primary.model, this.config.primary.provider);
|
|
1172
1523
|
if (!options.providerOverride && this.getProviderChain().length === 0) {
|
|
1173
1524
|
if (this.config.mockSimulation) {
|
|
1174
1525
|
const simResult = this.executeSimulation(sanitizedQuery, options.systemPrompt);
|
|
@@ -1184,11 +1535,11 @@ var AvantGateControlLayer = class {
|
|
|
1184
1535
|
this.checkPostExecutionBudget(result.tokens.total, result.costUSD);
|
|
1185
1536
|
await this.notifyAuditSink(result);
|
|
1186
1537
|
return result;
|
|
1187
|
-
}
|
|
1538
|
+
};
|
|
1188
1539
|
/**
|
|
1189
1540
|
* Exécute une requête et valide/répare le résultat selon un schéma Zod.
|
|
1190
1541
|
*/
|
|
1191
|
-
async
|
|
1542
|
+
executeStructured = async (options) => {
|
|
1192
1543
|
const rawResult = await this.execute({
|
|
1193
1544
|
userQuery: options.userQuery,
|
|
1194
1545
|
systemPrompt: options.systemPrompt,
|
|
@@ -1210,12 +1561,12 @@ var AvantGateControlLayer = class {
|
|
|
1210
1561
|
modelUsed: rawResult.modelUsed,
|
|
1211
1562
|
failoverOccurred: rawResult.failoverOccurred
|
|
1212
1563
|
};
|
|
1213
|
-
}
|
|
1564
|
+
};
|
|
1214
1565
|
/**
|
|
1215
1566
|
* Méthode unifiée de premier niveau pour l'extraction structurée sans code boilerplate.
|
|
1216
1567
|
* Gère le failover multi-fournisseurs, les retries, la validation Zod et la normalisation financière.
|
|
1217
1568
|
*/
|
|
1218
|
-
async
|
|
1569
|
+
generateStructuredOutput = async (options) => {
|
|
1219
1570
|
const maxRetries = options.maxRetries ?? this.config.retryOptions?.maxRetries ?? 2;
|
|
1220
1571
|
const modelToUse = options.model ?? this.config.primary.model;
|
|
1221
1572
|
const processedMessages = options.messages.map((msg) => {
|
|
@@ -1226,7 +1577,7 @@ var AvantGateControlLayer = class {
|
|
|
1226
1577
|
});
|
|
1227
1578
|
const promptLength = processedMessages.reduce((sum, msg) => sum + msg.content.length, 0);
|
|
1228
1579
|
const estimatedPromptTokens = Math.ceil(promptLength / 4);
|
|
1229
|
-
this.checkPreflightBudget(estimatedPromptTokens, modelToUse);
|
|
1580
|
+
await this.checkPreflightBudget(estimatedPromptTokens, modelToUse);
|
|
1230
1581
|
let lastError;
|
|
1231
1582
|
let accumulatedPromptTokens = 0;
|
|
1232
1583
|
let accumulatedCompletionTokens = 0;
|
|
@@ -1274,10 +1625,11 @@ var AvantGateControlLayer = class {
|
|
|
1274
1625
|
}
|
|
1275
1626
|
accumulatedPromptTokens += attemptPromptTokens;
|
|
1276
1627
|
accumulatedCompletionTokens += attemptCompletionTokens;
|
|
1628
|
+
const sanitizedResponse = this.applyOutputSecurityGuards(responseText);
|
|
1277
1629
|
const isFinancial = options.financialNormalizer ?? Boolean(
|
|
1278
1630
|
this.config.features?.finance?.enableFrenchAccounting || this.config.features?.finance
|
|
1279
1631
|
);
|
|
1280
|
-
const parsedData = validateWithZod(
|
|
1632
|
+
const parsedData = validateWithZod(sanitizedResponse, options.schema, {
|
|
1281
1633
|
financialNormalizer: isFinancial,
|
|
1282
1634
|
jurisdiction: this.config.features?.finance?.jurisdiction
|
|
1283
1635
|
});
|
|
@@ -1286,7 +1638,7 @@ var AvantGateControlLayer = class {
|
|
|
1286
1638
|
this.checkPostExecutionBudget(totalTokens, costUSD);
|
|
1287
1639
|
const result = {
|
|
1288
1640
|
data: parsedData,
|
|
1289
|
-
rawText:
|
|
1641
|
+
rawText: sanitizedResponse,
|
|
1290
1642
|
tokens: {
|
|
1291
1643
|
prompt: accumulatedPromptTokens,
|
|
1292
1644
|
completion: accumulatedCompletionTokens,
|
|
@@ -1314,11 +1666,11 @@ var AvantGateControlLayer = class {
|
|
|
1314
1666
|
}
|
|
1315
1667
|
}
|
|
1316
1668
|
throw lastError;
|
|
1317
|
-
}
|
|
1669
|
+
};
|
|
1318
1670
|
};
|
|
1319
|
-
|
|
1671
|
+
var createLLMControlLayer = (config) => {
|
|
1320
1672
|
return new AvantGateControlLayer(config);
|
|
1321
|
-
}
|
|
1673
|
+
};
|
|
1322
1674
|
var createAvantGate = createLLMControlLayer;
|
|
1323
1675
|
|
|
1324
1676
|
// src/prompts/prompt-template.ts
|
|
@@ -1741,7 +2093,23 @@ function auditToolResult(data, options) {
|
|
|
1741
2093
|
}
|
|
1742
2094
|
|
|
1743
2095
|
// src/agent/isolated-tool.ts
|
|
1744
|
-
|
|
2096
|
+
var verifyRoles = (configuredRoles, context, toolIdentifier) => {
|
|
2097
|
+
if (!configuredRoles || configuredRoles.length === 0) {
|
|
2098
|
+
return;
|
|
2099
|
+
}
|
|
2100
|
+
const userRoles = [
|
|
2101
|
+
...context.roles ?? [],
|
|
2102
|
+
...context.role ? [context.role] : []
|
|
2103
|
+
];
|
|
2104
|
+
const hasAllowedRole = configuredRoles.some((role) => userRoles.includes(role));
|
|
2105
|
+
if (!hasAllowedRole) {
|
|
2106
|
+
throw new ToolAccessDeniedError(
|
|
2107
|
+
toolIdentifier,
|
|
2108
|
+
"Access denied: insufficient role permissions"
|
|
2109
|
+
);
|
|
2110
|
+
}
|
|
2111
|
+
};
|
|
2112
|
+
var verifyDataAccessGuard = async (guard, args, context, toolIdentifier) => {
|
|
1745
2113
|
if (!guard) {
|
|
1746
2114
|
return;
|
|
1747
2115
|
}
|
|
@@ -1749,8 +2117,55 @@ async function verifyDataAccessGuard(guard, args, context, toolIdentifier) {
|
|
|
1749
2117
|
if (!isAllowed) {
|
|
1750
2118
|
throw new ToolAccessDeniedError(toolIdentifier, "Access denied by data access guard");
|
|
1751
2119
|
}
|
|
1752
|
-
}
|
|
1753
|
-
|
|
2120
|
+
};
|
|
2121
|
+
var verifyTenantIsolation = (assertTenant, rawResult, context, toolIdentifier) => {
|
|
2122
|
+
if (!assertTenant) {
|
|
2123
|
+
return;
|
|
2124
|
+
}
|
|
2125
|
+
if (!context.tenantId) {
|
|
2126
|
+
throw new ToolAccessDeniedError(
|
|
2127
|
+
toolIdentifier,
|
|
2128
|
+
"Access denied: missing session tenantId in execution context for multi-tenant tool"
|
|
2129
|
+
);
|
|
2130
|
+
}
|
|
2131
|
+
const recordTenantId = assertTenant(rawResult);
|
|
2132
|
+
if (recordTenantId && recordTenantId !== context.tenantId) {
|
|
2133
|
+
throw new ToolAccessDeniedError(
|
|
2134
|
+
toolIdentifier,
|
|
2135
|
+
`Cross-tenant IDOR access violation: record tenant '${recordTenantId}' does not match session tenant '${context.tenantId}'`
|
|
2136
|
+
);
|
|
2137
|
+
}
|
|
2138
|
+
};
|
|
2139
|
+
var verifyResourceOwnership = async (assertOwnership, rawResult, context, toolIdentifier) => {
|
|
2140
|
+
if (!assertOwnership) {
|
|
2141
|
+
return;
|
|
2142
|
+
}
|
|
2143
|
+
const isOwner = await assertOwnership(rawResult, context);
|
|
2144
|
+
if (!isOwner) {
|
|
2145
|
+
throw new ToolAccessDeniedError(
|
|
2146
|
+
toolIdentifier,
|
|
2147
|
+
"Ownership IDOR access violation: user does not own or have access to this resource"
|
|
2148
|
+
);
|
|
2149
|
+
}
|
|
2150
|
+
};
|
|
2151
|
+
var checkSuspiciousParameters = (parameters, toolName) => {
|
|
2152
|
+
if (!parameters || typeof parameters !== "object") {
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
const zodShape = parameters.shape;
|
|
2156
|
+
if (!zodShape || typeof zodShape !== "object") {
|
|
2157
|
+
return;
|
|
2158
|
+
}
|
|
2159
|
+
const suspiciousKeys = ["tenantId", "tenant_id", "ownerTenantId"];
|
|
2160
|
+
for (const key of suspiciousKeys) {
|
|
2161
|
+
if (key in zodShape) {
|
|
2162
|
+
console.warn(
|
|
2163
|
+
`[avantGate Security Warning] Tool '${toolName}' defines '${key}' as a model parameter. Tenancy should be inferred from the trusted server context (context.tenantId) rather than supplied by the untrusted model to prevent IDOR attacks.`
|
|
2164
|
+
);
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2168
|
+
var resolveInvalidationTags = async (config, args, rawResult, context) => {
|
|
1754
2169
|
if (!config.invalidationTags) {
|
|
1755
2170
|
return void 0;
|
|
1756
2171
|
}
|
|
@@ -1759,33 +2174,29 @@ async function resolveInvalidationTags(config, args, rawResult, context) {
|
|
|
1759
2174
|
await context.onInvalidationTags(tags);
|
|
1760
2175
|
}
|
|
1761
2176
|
return tags;
|
|
1762
|
-
}
|
|
1763
|
-
async
|
|
2177
|
+
};
|
|
2178
|
+
var dispatchClientData = async (rawResult, callback) => {
|
|
1764
2179
|
if (!callback) {
|
|
1765
2180
|
return;
|
|
1766
2181
|
}
|
|
1767
2182
|
await callback(rawResult);
|
|
1768
|
-
}
|
|
1769
|
-
async
|
|
2183
|
+
};
|
|
2184
|
+
var produceLlmPayload = async (rawResult, args, context, transformer) => {
|
|
1770
2185
|
if (transformer) {
|
|
1771
2186
|
return await transformer(rawResult, args, context);
|
|
1772
2187
|
}
|
|
1773
2188
|
return rawResult;
|
|
1774
|
-
}
|
|
1775
|
-
|
|
2189
|
+
};
|
|
2190
|
+
var validateLlmDto = (payload, schema, toolIdentifier) => {
|
|
1776
2191
|
const parseResult = schema.safeParse(payload);
|
|
1777
2192
|
if (!parseResult.success) {
|
|
1778
2193
|
const issues = parseResult.error.issues ?? [];
|
|
1779
2194
|
const errorMessages = issues.map((issue) => `${issue.path.join(".") || "root"}: ${issue.message}`).join(", ");
|
|
1780
|
-
throw new DtoValidationError(
|
|
1781
|
-
toolIdentifier,
|
|
1782
|
-
errorMessages,
|
|
1783
|
-
issues
|
|
1784
|
-
);
|
|
2195
|
+
throw new DtoValidationError(toolIdentifier, errorMessages, issues);
|
|
1785
2196
|
}
|
|
1786
2197
|
return parseResult.data;
|
|
1787
|
-
}
|
|
1788
|
-
|
|
2198
|
+
};
|
|
2199
|
+
var protectLlmPayload = (payload, toolIdentifier, sanitizePii = true, throwOnPii = false) => {
|
|
1789
2200
|
if (!sanitizePii) {
|
|
1790
2201
|
return { sanitized: payload, count: 0 };
|
|
1791
2202
|
}
|
|
@@ -1794,8 +2205,8 @@ function protectLlmPayload(payload, toolIdentifier, sanitizePii = true, throwOnP
|
|
|
1794
2205
|
throwOnPii
|
|
1795
2206
|
});
|
|
1796
2207
|
return { sanitized: sanitizedData, count: maskedCount };
|
|
1797
|
-
}
|
|
1798
|
-
async
|
|
2208
|
+
};
|
|
2209
|
+
var processLlmPayload = async (rawResult, args, context, config, toolIdentifier) => {
|
|
1799
2210
|
let payload = await produceLlmPayload(rawResult, args, context, config.llmDto);
|
|
1800
2211
|
if (config.llmDtoSchema) {
|
|
1801
2212
|
payload = validateLlmDto(payload, config.llmDtoSchema, toolIdentifier);
|
|
@@ -1806,14 +2217,15 @@ async function processLlmPayload(rawResult, args, context, config, toolIdentifie
|
|
|
1806
2217
|
config.sanitizePii !== false,
|
|
1807
2218
|
config.throwOnPii === true
|
|
1808
2219
|
);
|
|
1809
|
-
}
|
|
1810
|
-
|
|
2220
|
+
};
|
|
2221
|
+
var createIsolatedTool = (config) => {
|
|
1811
2222
|
const toolId = config.id || config.name;
|
|
1812
2223
|
const toolAlias = config.alias;
|
|
1813
2224
|
const clientCallback = config.clientDto;
|
|
1814
2225
|
const toolIdentifier = toolAlias || config.name;
|
|
1815
2226
|
const toolImpact = config.impact ?? "READ_ONLY";
|
|
1816
2227
|
const requireApproval = config.requireApproval ?? toolImpact === "DESTRUCTIVE";
|
|
2228
|
+
checkSuspiciousParameters(config.parameters, config.name);
|
|
1817
2229
|
const tool = {
|
|
1818
2230
|
description: config.description,
|
|
1819
2231
|
parameters: config.parameters,
|
|
@@ -1829,11 +2241,12 @@ function createIsolatedTool(config) {
|
|
|
1829
2241
|
_requireApproval: requireApproval,
|
|
1830
2242
|
_lastPiiFilteredCount: 0,
|
|
1831
2243
|
_lastInvalidationTags: void 0,
|
|
1832
|
-
async
|
|
2244
|
+
execute: async (args, context) => {
|
|
1833
2245
|
const updatedContext = {
|
|
1834
2246
|
...context,
|
|
1835
2247
|
callChain: context?.callChain ?? Object.freeze([toolId])
|
|
1836
2248
|
};
|
|
2249
|
+
verifyRoles(config.roles, updatedContext, toolIdentifier);
|
|
1837
2250
|
await verifyDataAccessGuard(
|
|
1838
2251
|
config.dataAccessGuard,
|
|
1839
2252
|
args,
|
|
@@ -1841,6 +2254,13 @@ function createIsolatedTool(config) {
|
|
|
1841
2254
|
toolIdentifier
|
|
1842
2255
|
);
|
|
1843
2256
|
const rawResult = await config.execute(args, updatedContext);
|
|
2257
|
+
verifyTenantIsolation(config.assertTenant, rawResult, updatedContext, toolIdentifier);
|
|
2258
|
+
await verifyResourceOwnership(
|
|
2259
|
+
config.assertOwnership,
|
|
2260
|
+
rawResult,
|
|
2261
|
+
updatedContext,
|
|
2262
|
+
toolIdentifier
|
|
2263
|
+
);
|
|
1844
2264
|
const tags = await resolveInvalidationTags(
|
|
1845
2265
|
config,
|
|
1846
2266
|
args,
|
|
@@ -1863,7 +2283,7 @@ function createIsolatedTool(config) {
|
|
|
1863
2283
|
}
|
|
1864
2284
|
};
|
|
1865
2285
|
return tool;
|
|
1866
|
-
}
|
|
2286
|
+
};
|
|
1867
2287
|
|
|
1868
2288
|
// src/workflow/errors.ts
|
|
1869
2289
|
var WorkflowSagaRollbackError = class extends Error {
|
|
@@ -2159,6 +2579,7 @@ var defineWorkflow = (config) => {
|
|
|
2159
2579
|
AvantGateBudgetExceededError,
|
|
2160
2580
|
AvantGateConfigurationError,
|
|
2161
2581
|
AvantGateControlLayer,
|
|
2582
|
+
AvantGateSecretLeakBlockedError,
|
|
2162
2583
|
BudgetExceededError,
|
|
2163
2584
|
CachedPricingAdapter,
|
|
2164
2585
|
ConfigurationError,
|
|
@@ -2169,10 +2590,13 @@ var defineWorkflow = (config) => {
|
|
|
2169
2590
|
PromptRegistry,
|
|
2170
2591
|
PromptTemplate,
|
|
2171
2592
|
SEED_MODEL_PRICES,
|
|
2593
|
+
SecretLeakBlockedError,
|
|
2594
|
+
SqlitePricingAdapter,
|
|
2172
2595
|
WorkflowAbortSignal,
|
|
2173
2596
|
WorkflowSagaRollbackError,
|
|
2174
2597
|
WorkflowValidationError,
|
|
2175
2598
|
ZenLLMControlLayer,
|
|
2599
|
+
applyOutputGuards,
|
|
2176
2600
|
calculateCostUSD,
|
|
2177
2601
|
createAvantGate,
|
|
2178
2602
|
createHttpProviderClient,
|
|
@@ -2184,6 +2608,7 @@ var defineWorkflow = (config) => {
|
|
|
2184
2608
|
resolveModelPrice,
|
|
2185
2609
|
resolveModelPriceAsync,
|
|
2186
2610
|
sanitizePII,
|
|
2611
|
+
sanitizeSecrets,
|
|
2187
2612
|
validateUserInput,
|
|
2188
2613
|
validateWithZod
|
|
2189
2614
|
});
|