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.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
extractAndCleanJSON,
|
|
3
3
|
validateWithZod
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-36ULFTM2.mjs";
|
|
5
5
|
import {
|
|
6
6
|
WorkflowAbortSignal,
|
|
7
7
|
WorkflowSagaRollbackError,
|
|
@@ -9,10 +9,13 @@ import {
|
|
|
9
9
|
defineWorkflow,
|
|
10
10
|
executeWorkflow,
|
|
11
11
|
isWorkflowAbortSignal
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-AH5XXSS4.mjs";
|
|
13
13
|
import {
|
|
14
14
|
sanitizePII
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-W27A25DK.mjs";
|
|
16
|
+
import {
|
|
17
|
+
__require
|
|
18
|
+
} from "./chunk-Y6FXYEAI.mjs";
|
|
16
19
|
|
|
17
20
|
// src/types.ts
|
|
18
21
|
var ConfigurationError = class extends Error {
|
|
@@ -27,6 +30,221 @@ var BudgetExceededError = class extends Error {
|
|
|
27
30
|
this.name = "BudgetExceededError";
|
|
28
31
|
}
|
|
29
32
|
};
|
|
33
|
+
var SecretLeakBlockedError = class extends Error {
|
|
34
|
+
detections;
|
|
35
|
+
constructor(message, detections) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = "SecretLeakBlockedError";
|
|
38
|
+
this.detections = detections;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// src/sqlite-pricing-adapter.ts
|
|
43
|
+
import fs from "fs";
|
|
44
|
+
import path from "path";
|
|
45
|
+
var normalizeIdentifier = (val) => {
|
|
46
|
+
return val.trim().toLowerCase().replace(":", "/");
|
|
47
|
+
};
|
|
48
|
+
var SqlitePricingAdapter = class {
|
|
49
|
+
db;
|
|
50
|
+
tableName;
|
|
51
|
+
constructor(options = {}) {
|
|
52
|
+
this.tableName = options.tableName ?? "model_pricing";
|
|
53
|
+
this.db = options.db ?? this.initDatabase(options.dbPath ?? ":memory:");
|
|
54
|
+
this.initSchema();
|
|
55
|
+
if (options.autoSeed) {
|
|
56
|
+
this.seedDefaultPrices(false);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
initDatabase = (dbPath) => {
|
|
60
|
+
if (dbPath !== ":memory:") {
|
|
61
|
+
const dir = path.dirname(path.resolve(dbPath));
|
|
62
|
+
if (!fs.existsSync(dir)) {
|
|
63
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const { DatabaseSync } = __require("sqlite");
|
|
68
|
+
return new DatabaseSync(dbPath);
|
|
69
|
+
} catch (err) {
|
|
70
|
+
throw new ConfigurationError(
|
|
71
|
+
`[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)}`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
initSchema = () => {
|
|
76
|
+
this.db.exec(`
|
|
77
|
+
CREATE TABLE IF NOT EXISTS ${this.tableName} (
|
|
78
|
+
provider TEXT NOT NULL,
|
|
79
|
+
model TEXT NOT NULL,
|
|
80
|
+
prompt_usd_per_million REAL NOT NULL,
|
|
81
|
+
completion_usd_per_million REAL NOT NULL,
|
|
82
|
+
cache_hit_usd_per_million REAL,
|
|
83
|
+
is_active INTEGER DEFAULT 1,
|
|
84
|
+
updated_at TEXT NOT NULL,
|
|
85
|
+
PRIMARY KEY (provider, model)
|
|
86
|
+
);
|
|
87
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tableName}_lookup
|
|
88
|
+
ON ${this.tableName}(provider, model, is_active);
|
|
89
|
+
`);
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Implémentation du port avantGate PricingAdapter.
|
|
93
|
+
* Résolution hiérarchique : (provider, model) -> (provider/model) -> (model).
|
|
94
|
+
*/
|
|
95
|
+
fetchPrice = (model, provider) => {
|
|
96
|
+
const normModel = normalizeIdentifier(model);
|
|
97
|
+
const normProvider = provider ? normalizeIdentifier(provider) : void 0;
|
|
98
|
+
if (normProvider) {
|
|
99
|
+
const row = this.queryActiveRow(normProvider, normModel);
|
|
100
|
+
if (row) return this.mapRowToPrice(row);
|
|
101
|
+
}
|
|
102
|
+
if (normModel.includes("/")) {
|
|
103
|
+
const firstSlashIdx = normModel.indexOf("/");
|
|
104
|
+
const extractedProvider = normModel.slice(0, firstSlashIdx);
|
|
105
|
+
const extractedModel = normModel.slice(firstSlashIdx + 1);
|
|
106
|
+
const splitRow = this.queryActiveRow(extractedProvider, extractedModel);
|
|
107
|
+
if (splitRow) return this.mapRowToPrice(splitRow);
|
|
108
|
+
}
|
|
109
|
+
const directRow = this.queryByModelOnly(normModel);
|
|
110
|
+
if (directRow) return this.mapRowToPrice(directRow);
|
|
111
|
+
return void 0;
|
|
112
|
+
};
|
|
113
|
+
queryActiveRow = (provider, model) => {
|
|
114
|
+
const stmt = this.db.prepare(`
|
|
115
|
+
SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
|
|
116
|
+
FROM ${this.tableName}
|
|
117
|
+
WHERE provider = ? AND model = ? AND is_active = 1
|
|
118
|
+
LIMIT 1
|
|
119
|
+
`);
|
|
120
|
+
return stmt.get(provider, model);
|
|
121
|
+
};
|
|
122
|
+
queryByModelOnly = (model) => {
|
|
123
|
+
const stmt = this.db.prepare(`
|
|
124
|
+
SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
|
|
125
|
+
FROM ${this.tableName}
|
|
126
|
+
WHERE model = ? AND is_active = 1
|
|
127
|
+
LIMIT 1
|
|
128
|
+
`);
|
|
129
|
+
return stmt.get(model);
|
|
130
|
+
};
|
|
131
|
+
mapRowToPrice = (row) => {
|
|
132
|
+
return {
|
|
133
|
+
promptUSDPerMillion: Number(row.prompt_usd_per_million),
|
|
134
|
+
completionUSDPerMillion: Number(row.completion_usd_per_million),
|
|
135
|
+
cacheHitUSDPerMillion: row.cache_hit_usd_per_million != null ? Number(row.cache_hit_usd_per_million) : void 0
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Enregistre ou met à jour la tarification d'un modèle.
|
|
140
|
+
*/
|
|
141
|
+
upsertPrice = (provider, model, price, isActive = true) => {
|
|
142
|
+
const normProvider = normalizeIdentifier(provider);
|
|
143
|
+
const normModel = normalizeIdentifier(model);
|
|
144
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
145
|
+
const stmt = this.db.prepare(`
|
|
146
|
+
INSERT OR REPLACE INTO ${this.tableName}
|
|
147
|
+
(provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at)
|
|
148
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
149
|
+
`);
|
|
150
|
+
stmt.run(
|
|
151
|
+
normProvider,
|
|
152
|
+
normModel,
|
|
153
|
+
price.promptUSDPerMillion,
|
|
154
|
+
price.completionUSDPerMillion,
|
|
155
|
+
price.cacheHitUSDPerMillion ?? null,
|
|
156
|
+
isActive ? 1 : 0,
|
|
157
|
+
now
|
|
158
|
+
);
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Supprime une règle tarifaire de la base de données.
|
|
162
|
+
*/
|
|
163
|
+
deletePrice = (provider, model) => {
|
|
164
|
+
const normProvider = normalizeIdentifier(provider);
|
|
165
|
+
const normModel = normalizeIdentifier(model);
|
|
166
|
+
const stmt = this.db.prepare(`
|
|
167
|
+
DELETE FROM ${this.tableName}
|
|
168
|
+
WHERE provider = ? AND model = ?
|
|
169
|
+
`);
|
|
170
|
+
const result = stmt.run(normProvider, normModel);
|
|
171
|
+
return Boolean(result && typeof result.changes === "number" && result.changes > 0);
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Retourne l'ensemble des règles tarifaires enregistrées.
|
|
175
|
+
*/
|
|
176
|
+
getAllPrices = () => {
|
|
177
|
+
const stmt = this.db.prepare(`
|
|
178
|
+
SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
|
|
179
|
+
FROM ${this.tableName}
|
|
180
|
+
ORDER BY provider ASC, model ASC
|
|
181
|
+
`);
|
|
182
|
+
const rows = stmt.all();
|
|
183
|
+
return rows.map((row) => ({
|
|
184
|
+
provider: row.provider,
|
|
185
|
+
model: row.model,
|
|
186
|
+
price: this.mapRowToPrice(row),
|
|
187
|
+
isActive: Boolean(row.is_active),
|
|
188
|
+
updatedAt: row.updated_at
|
|
189
|
+
}));
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Remplit la base avec le catalogue de départ SEED_MODEL_PRICES.
|
|
193
|
+
* @param force Si true, écrase les tarifs existants. Si false, insère uniquement les modèles manquants.
|
|
194
|
+
* @returns Le nombre d'enregistrements insérés.
|
|
195
|
+
*/
|
|
196
|
+
seedDefaultPrices = (force = false) => {
|
|
197
|
+
const conflictClause = force ? "INSERT OR REPLACE" : "INSERT OR IGNORE";
|
|
198
|
+
const stmt = this.db.prepare(`
|
|
199
|
+
${conflictClause} INTO ${this.tableName}
|
|
200
|
+
(provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at)
|
|
201
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
202
|
+
`);
|
|
203
|
+
let inserted = 0;
|
|
204
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
205
|
+
for (const [key, price] of Object.entries(SEED_MODEL_PRICES)) {
|
|
206
|
+
const normKey = normalizeIdentifier(key);
|
|
207
|
+
let provider = "direct";
|
|
208
|
+
let model = normKey;
|
|
209
|
+
if (normKey.includes("/")) {
|
|
210
|
+
const slashIdx = normKey.indexOf("/");
|
|
211
|
+
provider = normKey.slice(0, slashIdx);
|
|
212
|
+
model = normKey.slice(slashIdx + 1);
|
|
213
|
+
}
|
|
214
|
+
const res1 = stmt.run(
|
|
215
|
+
provider,
|
|
216
|
+
model,
|
|
217
|
+
price.promptUSDPerMillion,
|
|
218
|
+
price.completionUSDPerMillion,
|
|
219
|
+
price.cacheHitUSDPerMillion ?? null,
|
|
220
|
+
1,
|
|
221
|
+
now
|
|
222
|
+
);
|
|
223
|
+
if (res1?.changes && res1.changes > 0) inserted++;
|
|
224
|
+
if (provider !== "direct") {
|
|
225
|
+
const res2 = stmt.run(
|
|
226
|
+
"direct",
|
|
227
|
+
normKey,
|
|
228
|
+
price.promptUSDPerMillion,
|
|
229
|
+
price.completionUSDPerMillion,
|
|
230
|
+
price.cacheHitUSDPerMillion ?? null,
|
|
231
|
+
1,
|
|
232
|
+
now
|
|
233
|
+
);
|
|
234
|
+
if (res2?.changes && res2.changes > 0) inserted++;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return inserted;
|
|
238
|
+
};
|
|
239
|
+
/**
|
|
240
|
+
* Ferme la connexion SQLite sous-jacente si supporté.
|
|
241
|
+
*/
|
|
242
|
+
close = () => {
|
|
243
|
+
if (typeof this.db.close === "function") {
|
|
244
|
+
this.db.close();
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
};
|
|
30
248
|
|
|
31
249
|
// src/pricing.ts
|
|
32
250
|
var SEED_MODEL_PRICES = {
|
|
@@ -52,9 +270,9 @@ var SEED_MODEL_PRICES = {
|
|
|
52
270
|
// Local Ollama (Toujours $0)
|
|
53
271
|
"ollama": { promptUSDPerMillion: 0, completionUSDPerMillion: 0 }
|
|
54
272
|
};
|
|
55
|
-
|
|
273
|
+
var normalizeKey = (identifier) => {
|
|
56
274
|
return identifier.trim().toLowerCase().replace(":", "/");
|
|
57
|
-
}
|
|
275
|
+
};
|
|
58
276
|
var CachedPricingAdapter = class {
|
|
59
277
|
cache = /* @__PURE__ */ new Map();
|
|
60
278
|
ttlMs;
|
|
@@ -63,7 +281,7 @@ var CachedPricingAdapter = class {
|
|
|
63
281
|
this.delegate = delegate;
|
|
64
282
|
this.ttlMs = ttlMs;
|
|
65
283
|
}
|
|
66
|
-
async
|
|
284
|
+
fetchPrice = async (model, provider) => {
|
|
67
285
|
const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
|
|
68
286
|
const cached = this.cache.get(key);
|
|
69
287
|
if (cached && Date.now() < cached.expiresAt) {
|
|
@@ -81,72 +299,76 @@ var CachedPricingAdapter = class {
|
|
|
81
299
|
}
|
|
82
300
|
}
|
|
83
301
|
return void 0;
|
|
84
|
-
}
|
|
85
|
-
peek(model, provider) {
|
|
302
|
+
};
|
|
303
|
+
peek = (model, provider) => {
|
|
86
304
|
const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
305
|
+
const cached = this.cache.get(key);
|
|
306
|
+
if (cached && Date.now() < cached.expiresAt) {
|
|
307
|
+
return cached.price;
|
|
308
|
+
}
|
|
309
|
+
return void 0;
|
|
310
|
+
};
|
|
311
|
+
clearCache = () => {
|
|
90
312
|
this.cache.clear();
|
|
91
|
-
}
|
|
92
|
-
invalidate(model, provider) {
|
|
313
|
+
};
|
|
314
|
+
invalidate = (model, provider) => {
|
|
93
315
|
if (!model) {
|
|
94
316
|
this.clearCache();
|
|
95
317
|
return;
|
|
96
318
|
}
|
|
97
319
|
const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
|
|
98
320
|
this.cache.delete(key);
|
|
99
|
-
}
|
|
321
|
+
};
|
|
100
322
|
};
|
|
101
|
-
var PricingRegistry = class {
|
|
323
|
+
var PricingRegistry = class _PricingRegistry {
|
|
102
324
|
static prices = /* @__PURE__ */ new Map();
|
|
103
325
|
static adapter;
|
|
104
|
-
static registerPrice(identifier, price) {
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
static registerDistributorPrices(distributor, priceMap) {
|
|
326
|
+
static registerPrice = (identifier, price) => {
|
|
327
|
+
_PricingRegistry.prices.set(normalizeKey(identifier), price);
|
|
328
|
+
};
|
|
329
|
+
static registerDistributorPrices = (distributor, priceMap) => {
|
|
108
330
|
for (const [model, price] of Object.entries(priceMap)) {
|
|
109
|
-
|
|
331
|
+
_PricingRegistry.prices.set(normalizeKey(`${distributor}/${model}`), price);
|
|
110
332
|
}
|
|
111
|
-
}
|
|
112
|
-
static registerPrices(prices) {
|
|
333
|
+
};
|
|
334
|
+
static registerPrices = (prices) => {
|
|
113
335
|
for (const [key, price] of Object.entries(prices)) {
|
|
114
|
-
|
|
336
|
+
_PricingRegistry.prices.set(normalizeKey(key), price);
|
|
115
337
|
}
|
|
116
|
-
}
|
|
117
|
-
static getPrice(model, provider) {
|
|
338
|
+
};
|
|
339
|
+
static getPrice = (model, provider) => {
|
|
118
340
|
if (provider) {
|
|
119
341
|
const distributorKey = normalizeKey(`${provider}/${model}`);
|
|
120
|
-
const directDistributorPrice =
|
|
342
|
+
const directDistributorPrice = _PricingRegistry.prices.get(distributorKey);
|
|
121
343
|
if (directDistributorPrice) {
|
|
122
344
|
return directDistributorPrice;
|
|
123
345
|
}
|
|
124
346
|
}
|
|
125
|
-
return
|
|
126
|
-
}
|
|
127
|
-
static setAdapter(adapter) {
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
static getAdapter() {
|
|
131
|
-
return
|
|
132
|
-
}
|
|
133
|
-
static clear() {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
347
|
+
return _PricingRegistry.prices.get(normalizeKey(model));
|
|
348
|
+
};
|
|
349
|
+
static setAdapter = (adapter) => {
|
|
350
|
+
_PricingRegistry.adapter = adapter;
|
|
351
|
+
};
|
|
352
|
+
static getAdapter = () => {
|
|
353
|
+
return _PricingRegistry.adapter;
|
|
354
|
+
};
|
|
355
|
+
static clear = () => {
|
|
356
|
+
_PricingRegistry.prices.clear();
|
|
357
|
+
_PricingRegistry.adapter = void 0;
|
|
358
|
+
};
|
|
137
359
|
/**
|
|
138
360
|
* Réinitialise le registre en chargeant le catalogue d'exemple SEED_MODEL_PRICES.
|
|
139
361
|
*/
|
|
140
|
-
static loadSeedPrices() {
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
static getAllPrices() {
|
|
362
|
+
static loadSeedPrices = () => {
|
|
363
|
+
_PricingRegistry.registerPrices(SEED_MODEL_PRICES);
|
|
364
|
+
};
|
|
365
|
+
static getAllPrices = () => {
|
|
144
366
|
const out = {};
|
|
145
|
-
for (const [k, v] of
|
|
367
|
+
for (const [k, v] of _PricingRegistry.prices.entries()) {
|
|
146
368
|
out[k] = v;
|
|
147
369
|
}
|
|
148
370
|
return out;
|
|
149
|
-
}
|
|
371
|
+
};
|
|
150
372
|
};
|
|
151
373
|
var DEFAULT_MODEL_PRICES = new Proxy({}, {
|
|
152
374
|
get(_target, prop) {
|
|
@@ -166,7 +388,7 @@ var DEFAULT_MODEL_PRICES = new Proxy({}, {
|
|
|
166
388
|
return void 0;
|
|
167
389
|
}
|
|
168
390
|
});
|
|
169
|
-
|
|
391
|
+
var resolveModelPrice = (model, options) => {
|
|
170
392
|
const opts = typeof options === "string" ? { provider: options } : options ?? {};
|
|
171
393
|
if (opts.providerPricing) {
|
|
172
394
|
return opts.providerPricing;
|
|
@@ -197,8 +419,8 @@ function resolveModelPrice(model, options) {
|
|
|
197
419
|
return { promptUSDPerMillion: 0, completionUSDPerMillion: 0 };
|
|
198
420
|
}
|
|
199
421
|
return void 0;
|
|
200
|
-
}
|
|
201
|
-
async
|
|
422
|
+
};
|
|
423
|
+
var resolveModelPriceAsync = async (model, options) => {
|
|
202
424
|
const opts = typeof options === "string" ? { provider: options } : options ?? {};
|
|
203
425
|
const activeAdapter = opts.adapter ?? PricingRegistry.getAdapter();
|
|
204
426
|
if (activeAdapter) {
|
|
@@ -211,8 +433,8 @@ async function resolveModelPriceAsync(model, options) {
|
|
|
211
433
|
}
|
|
212
434
|
}
|
|
213
435
|
return resolveModelPrice(model, options);
|
|
214
|
-
}
|
|
215
|
-
|
|
436
|
+
};
|
|
437
|
+
var calculateCostUSD = (model, promptTokens, completionTokens, cacheHitTokens = 0, options) => {
|
|
216
438
|
const pricing = resolveModelPrice(model, options);
|
|
217
439
|
if (!pricing) {
|
|
218
440
|
return 0;
|
|
@@ -221,7 +443,7 @@ function calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens
|
|
|
221
443
|
const completionCost = completionTokens / 1e6 * pricing.completionUSDPerMillion;
|
|
222
444
|
const cacheDiscount = cacheHitTokens && pricing.cacheHitUSDPerMillion ? cacheHitTokens / 1e6 * (pricing.promptUSDPerMillion - pricing.cacheHitUSDPerMillion) : 0;
|
|
223
445
|
return Math.max(0, promptCost + completionCost - cacheDiscount);
|
|
224
|
-
}
|
|
446
|
+
};
|
|
225
447
|
|
|
226
448
|
// src/input-guard.ts
|
|
227
449
|
var INJECTION_PATTERNS = [
|
|
@@ -232,7 +454,7 @@ var INJECTION_PATTERNS = [
|
|
|
232
454
|
/output\s+(?:your\s+)?(?:system\s+prompt|initial\s+instructions)/i,
|
|
233
455
|
/repeat\s+(?:the\s+words\s+above|everything\s+above)/i
|
|
234
456
|
];
|
|
235
|
-
|
|
457
|
+
var validateUserInput = (input, options) => {
|
|
236
458
|
const maxLength = options?.maxLength ?? 5e4;
|
|
237
459
|
if (input.length > maxLength) {
|
|
238
460
|
return {
|
|
@@ -251,7 +473,7 @@ function validateUserInput(input, options) {
|
|
|
251
473
|
}
|
|
252
474
|
}
|
|
253
475
|
return { valid: true };
|
|
254
|
-
}
|
|
476
|
+
};
|
|
255
477
|
|
|
256
478
|
// src/providers/http-client.ts
|
|
257
479
|
var DEFAULT_BASE_URLS = {
|
|
@@ -272,14 +494,14 @@ var HttpProviderClient = class {
|
|
|
272
494
|
this.apiKey = config.apiKey;
|
|
273
495
|
this.baseUrl = config.baseUrl || DEFAULT_BASE_URLS[config.provider] || "https://api.openai.com/v1";
|
|
274
496
|
}
|
|
275
|
-
resolveEndpoint() {
|
|
497
|
+
resolveEndpoint = () => {
|
|
276
498
|
const trimmed = this.baseUrl.replace(/\/+$/, "");
|
|
277
499
|
if (trimmed.endsWith("/chat/completions")) {
|
|
278
500
|
return trimmed;
|
|
279
501
|
}
|
|
280
502
|
return `${trimmed}/chat/completions`;
|
|
281
|
-
}
|
|
282
|
-
buildHeaders() {
|
|
503
|
+
};
|
|
504
|
+
buildHeaders = () => {
|
|
283
505
|
const headers = {
|
|
284
506
|
"Content-Type": "application/json"
|
|
285
507
|
};
|
|
@@ -291,8 +513,8 @@ var HttpProviderClient = class {
|
|
|
291
513
|
headers["X-Title"] = "AvantGate";
|
|
292
514
|
}
|
|
293
515
|
return headers;
|
|
294
|
-
}
|
|
295
|
-
buildPayload(options, model) {
|
|
516
|
+
};
|
|
517
|
+
buildPayload = (options, model) => {
|
|
296
518
|
return {
|
|
297
519
|
model,
|
|
298
520
|
messages: options.messages.map((m) => ({
|
|
@@ -302,8 +524,8 @@ var HttpProviderClient = class {
|
|
|
302
524
|
temperature: options.temperature ?? 0.2,
|
|
303
525
|
...options.responseFormat ? { response_format: options.responseFormat } : {}
|
|
304
526
|
};
|
|
305
|
-
}
|
|
306
|
-
extractUsage(usageData) {
|
|
527
|
+
};
|
|
528
|
+
extractUsage = (usageData) => {
|
|
307
529
|
if (!usageData) {
|
|
308
530
|
return void 0;
|
|
309
531
|
}
|
|
@@ -313,8 +535,8 @@ var HttpProviderClient = class {
|
|
|
313
535
|
totalTokens: usageData.total_tokens,
|
|
314
536
|
promptCacheHitTokens: usageData.prompt_tokens_details?.cached_tokens
|
|
315
537
|
};
|
|
316
|
-
}
|
|
317
|
-
async
|
|
538
|
+
};
|
|
539
|
+
complete = async (options) => {
|
|
318
540
|
const endpoint = this.resolveEndpoint();
|
|
319
541
|
const model = options.model ?? "default";
|
|
320
542
|
const headers = this.buildHeaders();
|
|
@@ -334,11 +556,114 @@ var HttpProviderClient = class {
|
|
|
334
556
|
const text = data.choices?.[0]?.message?.content ?? "";
|
|
335
557
|
const usage = this.extractUsage(data.usage);
|
|
336
558
|
return { text, usage };
|
|
337
|
-
}
|
|
559
|
+
};
|
|
338
560
|
};
|
|
339
|
-
|
|
561
|
+
var createHttpProviderClient = (config) => {
|
|
340
562
|
return new HttpProviderClient(config);
|
|
341
|
-
}
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
// src/secret-guard.ts
|
|
566
|
+
var SECRET_PATTERNS = [
|
|
567
|
+
// 1. Clés privées (RSA, OpenSSH, EC, DSA)
|
|
568
|
+
{
|
|
569
|
+
type: "PRIVATE_KEY",
|
|
570
|
+
regex: /-----BEGIN (?:[A-Z0-9_-]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z0-9_-]+ )?PRIVATE KEY-----/g,
|
|
571
|
+
mask: "[REDACTED_PRIVATE_KEY]"
|
|
572
|
+
},
|
|
573
|
+
// 2. Clés Anthropic Claude (avant OpenAI pour éviter collision sk-)
|
|
574
|
+
{
|
|
575
|
+
type: "ANTHROPIC_API_KEY",
|
|
576
|
+
regex: /\bsk-ant-(?:api\d{2}-)?[A-Za-z0-9_-]{20,}\b/g,
|
|
577
|
+
mask: "[REDACTED_ANTHROPIC_KEY]"
|
|
578
|
+
},
|
|
579
|
+
// 3. Clés OpenAI (standard, projet, admin) - exclut explicitement sk-ant-
|
|
580
|
+
{
|
|
581
|
+
type: "OPENAI_API_KEY",
|
|
582
|
+
regex: /\bsk-(?!ant-)(?:proj-|admin-|org-)?[A-Za-z0-9_-]{20,}\b/g,
|
|
583
|
+
mask: "[REDACTED_OPENAI_KEY]"
|
|
584
|
+
},
|
|
585
|
+
// 4. Clés AWS Access Key ID
|
|
586
|
+
{
|
|
587
|
+
type: "AWS_ACCESS_KEY",
|
|
588
|
+
regex: /\b(?:AKIA|ABIA|ACCA|ASIA)[0-9A-Z]{16}\b/g,
|
|
589
|
+
mask: "[REDACTED_AWS_KEY]"
|
|
590
|
+
},
|
|
591
|
+
// 5. Tokens GitHub (Personal, OAuth, User, Server)
|
|
592
|
+
{
|
|
593
|
+
type: "GITHUB_TOKEN",
|
|
594
|
+
regex: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{36,255}\b/g,
|
|
595
|
+
mask: "[REDACTED_GITHUB_TOKEN]"
|
|
596
|
+
},
|
|
597
|
+
// 6. JSON Web Tokens (JWT)
|
|
598
|
+
{
|
|
599
|
+
type: "JWT_TOKEN",
|
|
600
|
+
regex: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]{10,}\.[A-Za-z0-9._-]{10,}\b/g,
|
|
601
|
+
mask: "[REDACTED_JWT]"
|
|
602
|
+
},
|
|
603
|
+
// 7. Tokens Slack
|
|
604
|
+
{
|
|
605
|
+
type: "SLACK_TOKEN",
|
|
606
|
+
regex: /\bxox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,}\b/g,
|
|
607
|
+
mask: "[REDACTED_SLACK_TOKEN]"
|
|
608
|
+
},
|
|
609
|
+
// 8. Mots de passe dans URL de connexion (Postgres, Mongo, Redis, MySQL)
|
|
610
|
+
{
|
|
611
|
+
type: "DATABASE_CONNECTION_SECRET",
|
|
612
|
+
regex: /(?:postgres|postgresql|mysql|mongodb(?:\+srv)?|redis|amqp):\/\/[^:\s\/@]+:([^@\s]+)@/gi,
|
|
613
|
+
mask: "[REDACTED_DB_SECRET]"
|
|
614
|
+
}
|
|
615
|
+
];
|
|
616
|
+
var sanitizeSecrets = (input) => {
|
|
617
|
+
let text = input;
|
|
618
|
+
const detections = [];
|
|
619
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
620
|
+
let matchCount = 0;
|
|
621
|
+
if (pattern.type === "DATABASE_CONNECTION_SECRET") {
|
|
622
|
+
text = text.replace(pattern.regex, (fullMatch, password) => {
|
|
623
|
+
matchCount++;
|
|
624
|
+
return fullMatch.replace(password, pattern.mask);
|
|
625
|
+
});
|
|
626
|
+
} else {
|
|
627
|
+
text = text.replace(pattern.regex, () => {
|
|
628
|
+
matchCount++;
|
|
629
|
+
return pattern.mask;
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
if (matchCount > 0) {
|
|
633
|
+
detections.push({ type: pattern.type, matchedCount: matchCount });
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return {
|
|
637
|
+
text,
|
|
638
|
+
hasSecrets: detections.length > 0,
|
|
639
|
+
detections
|
|
640
|
+
};
|
|
641
|
+
};
|
|
642
|
+
var applyOutputGuards = (rawOutput, options) => {
|
|
643
|
+
let currentText = rawOutput;
|
|
644
|
+
let piiCount = 0;
|
|
645
|
+
if (options?.maskPII) {
|
|
646
|
+
const piiResult = sanitizePII(currentText);
|
|
647
|
+
currentText = piiResult.text;
|
|
648
|
+
piiCount = piiResult.maskedCount;
|
|
649
|
+
}
|
|
650
|
+
const secretResult = sanitizeSecrets(currentText);
|
|
651
|
+
currentText = secretResult.text;
|
|
652
|
+
if (secretResult.hasSecrets && options?.secretLeakAction === "BLOCK") {
|
|
653
|
+
const types = secretResult.detections.map((d) => d.type).join(", ");
|
|
654
|
+
throw new SecretLeakBlockedError(
|
|
655
|
+
`[AvantGate Output Guard] Response blocked: detected potential secret leak (${types})`,
|
|
656
|
+
secretResult.detections
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
return {
|
|
660
|
+
text: currentText,
|
|
661
|
+
sanitized: piiCount > 0 || secretResult.hasSecrets,
|
|
662
|
+
secretDetected: secretResult.hasSecrets,
|
|
663
|
+
piiMaskedCount: piiCount,
|
|
664
|
+
detections: secretResult.detections
|
|
665
|
+
};
|
|
666
|
+
};
|
|
342
667
|
|
|
343
668
|
// src/control-layer.ts
|
|
344
669
|
var AvantGateControlLayer = class {
|
|
@@ -358,7 +683,7 @@ var AvantGateControlLayer = class {
|
|
|
358
683
|
);
|
|
359
684
|
}
|
|
360
685
|
}
|
|
361
|
-
resolveProviderConfig(provider) {
|
|
686
|
+
resolveProviderConfig = (provider) => {
|
|
362
687
|
if (!provider) return void 0;
|
|
363
688
|
if (provider.client) return provider;
|
|
364
689
|
if (provider.apiKey || provider.baseUrl || provider.provider === "ollama") {
|
|
@@ -368,8 +693,8 @@ var AvantGateControlLayer = class {
|
|
|
368
693
|
};
|
|
369
694
|
}
|
|
370
695
|
return provider;
|
|
371
|
-
}
|
|
372
|
-
findProviderConfig(provider, model) {
|
|
696
|
+
};
|
|
697
|
+
findProviderConfig = (provider, model) => {
|
|
373
698
|
const list = [this.config.primary, this.config.fallback, this.config.emergencyFallback].filter(
|
|
374
699
|
(p) => Boolean(p)
|
|
375
700
|
);
|
|
@@ -382,8 +707,8 @@ var AvantGateControlLayer = class {
|
|
|
382
707
|
if (matchModel) return matchModel;
|
|
383
708
|
}
|
|
384
709
|
return this.config.primary;
|
|
385
|
-
}
|
|
386
|
-
calculateCost(model, promptTokens, completionTokens, cacheHitTokens = 0, provider) {
|
|
710
|
+
};
|
|
711
|
+
calculateCost = (model, promptTokens, completionTokens, cacheHitTokens = 0, provider) => {
|
|
387
712
|
const providerCfg = this.findProviderConfig(provider, model);
|
|
388
713
|
return calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens, {
|
|
389
714
|
provider: provider ?? providerCfg?.provider,
|
|
@@ -391,8 +716,8 @@ var AvantGateControlLayer = class {
|
|
|
391
716
|
customPricing: this.config.customPricing,
|
|
392
717
|
adapter: this.cachedPricingAdapter
|
|
393
718
|
});
|
|
394
|
-
}
|
|
395
|
-
applySecurityGuards(userQuery) {
|
|
719
|
+
};
|
|
720
|
+
applySecurityGuards = (userQuery) => {
|
|
396
721
|
const guard = validateUserInput(userQuery, {
|
|
397
722
|
detectInjection: this.config.security?.detectPromptInjection,
|
|
398
723
|
maxLength: this.config.security?.maxInputLength
|
|
@@ -404,8 +729,21 @@ var AvantGateControlLayer = class {
|
|
|
404
729
|
return sanitizePII(userQuery).text;
|
|
405
730
|
}
|
|
406
731
|
return userQuery;
|
|
407
|
-
}
|
|
408
|
-
|
|
732
|
+
};
|
|
733
|
+
applyOutputSecurityGuards = (rawOutput) => {
|
|
734
|
+
const isDlpEnabled = this.config.security?.outputDLP ?? this.config.security?.maskPII ?? false;
|
|
735
|
+
const shouldCheckSecrets = this.config.security?.blockSecretLeaks ?? true;
|
|
736
|
+
if (!isDlpEnabled && !shouldCheckSecrets) {
|
|
737
|
+
return rawOutput;
|
|
738
|
+
}
|
|
739
|
+
const guardResult = applyOutputGuards(rawOutput, {
|
|
740
|
+
maskPII: isDlpEnabled,
|
|
741
|
+
blockSecretLeaks: shouldCheckSecrets,
|
|
742
|
+
secretLeakAction: this.config.security?.secretLeakAction ?? "REDACT"
|
|
743
|
+
});
|
|
744
|
+
return guardResult.text;
|
|
745
|
+
};
|
|
746
|
+
checkPreflightBudget = async (estimatedPromptTokens, targetModel, provider) => {
|
|
409
747
|
if (this.config.maxTokenBudget !== void 0 && estimatedPromptTokens > this.config.maxTokenBudget) {
|
|
410
748
|
throw new BudgetExceededError(
|
|
411
749
|
`[AvantGate Budget Guard] Pre-flight token budget exceeded: estimated prompt (${estimatedPromptTokens} tokens) exceeds maxTokenBudget (${this.config.maxTokenBudget}).`
|
|
@@ -414,7 +752,7 @@ var AvantGateControlLayer = class {
|
|
|
414
752
|
if (this.config.maxCostUSD !== void 0) {
|
|
415
753
|
const isLocalFree = provider === "ollama" || targetModel.toLowerCase().includes("ollama");
|
|
416
754
|
const providerCfg = this.findProviderConfig(provider, targetModel);
|
|
417
|
-
const price =
|
|
755
|
+
const price = await resolveModelPriceAsync(targetModel, {
|
|
418
756
|
provider: provider ?? providerCfg?.provider,
|
|
419
757
|
providerPricing: providerCfg?.pricing,
|
|
420
758
|
customPricing: this.config.customPricing,
|
|
@@ -432,8 +770,8 @@ var AvantGateControlLayer = class {
|
|
|
432
770
|
);
|
|
433
771
|
}
|
|
434
772
|
}
|
|
435
|
-
}
|
|
436
|
-
checkPostExecutionBudget(tokensTotal, costUSD) {
|
|
773
|
+
};
|
|
774
|
+
checkPostExecutionBudget = (tokensTotal, costUSD) => {
|
|
437
775
|
if (this.config.maxTokenBudget !== void 0 && tokensTotal > this.config.maxTokenBudget) {
|
|
438
776
|
throw new BudgetExceededError(
|
|
439
777
|
`[AvantGate Budget Guard] Execution total tokens (${tokensTotal}) exceeded maxTokenBudget (${this.config.maxTokenBudget}).`
|
|
@@ -444,16 +782,16 @@ var AvantGateControlLayer = class {
|
|
|
444
782
|
`[AvantGate Budget Guard] Execution cost ($${costUSD.toFixed(6)}) exceeded maxCostUSD ($${this.config.maxCostUSD}).`
|
|
445
783
|
);
|
|
446
784
|
}
|
|
447
|
-
}
|
|
448
|
-
buildMessages(systemPrompt, query) {
|
|
785
|
+
};
|
|
786
|
+
buildMessages = (systemPrompt, query) => {
|
|
449
787
|
const messages = [];
|
|
450
788
|
if (systemPrompt) {
|
|
451
789
|
messages.push({ role: "system", content: systemPrompt });
|
|
452
790
|
}
|
|
453
791
|
messages.push({ role: "user", content: query });
|
|
454
792
|
return messages;
|
|
455
|
-
}
|
|
456
|
-
resolveTokens(rawUsage, query, text) {
|
|
793
|
+
};
|
|
794
|
+
resolveTokens = (rawUsage, query, text) => {
|
|
457
795
|
const promptTokens = rawUsage?.promptTokens ?? Math.ceil(query.length / 4);
|
|
458
796
|
const completionTokens = rawUsage?.completionTokens ?? Math.ceil(text.length / 4);
|
|
459
797
|
const totalTokens = rawUsage?.totalTokens ?? promptTokens + completionTokens;
|
|
@@ -463,15 +801,15 @@ var AvantGateControlLayer = class {
|
|
|
463
801
|
totalTokens,
|
|
464
802
|
promptCacheHitTokens: rawUsage?.promptCacheHitTokens
|
|
465
803
|
};
|
|
466
|
-
}
|
|
467
|
-
getProviderChain() {
|
|
804
|
+
};
|
|
805
|
+
getProviderChain = () => {
|
|
468
806
|
return [
|
|
469
807
|
this.config.primary,
|
|
470
808
|
this.config.fallback,
|
|
471
809
|
this.config.emergencyFallback
|
|
472
810
|
].filter((provider) => Boolean(provider?.client));
|
|
473
|
-
}
|
|
474
|
-
executeSimulation(query, systemPrompt) {
|
|
811
|
+
};
|
|
812
|
+
executeSimulation = (query, systemPrompt) => {
|
|
475
813
|
const promptTokens = Math.ceil(((systemPrompt?.length ?? 0) + query.length) / 4);
|
|
476
814
|
const completionTokens = 50;
|
|
477
815
|
const cost = this.calculateCost(this.config.primary.model, promptTokens, completionTokens);
|
|
@@ -487,8 +825,8 @@ var AvantGateControlLayer = class {
|
|
|
487
825
|
failoverOccurred: false,
|
|
488
826
|
attempts: 1
|
|
489
827
|
};
|
|
490
|
-
}
|
|
491
|
-
async
|
|
828
|
+
};
|
|
829
|
+
executeOverrideProvider = async (provider, messages, query, temperature) => {
|
|
492
830
|
const response = await provider.complete({
|
|
493
831
|
model: this.config.primary.model,
|
|
494
832
|
messages,
|
|
@@ -502,8 +840,8 @@ var AvantGateControlLayer = class {
|
|
|
502
840
|
failoverOccurred: false,
|
|
503
841
|
attempts: 1
|
|
504
842
|
};
|
|
505
|
-
}
|
|
506
|
-
async
|
|
843
|
+
};
|
|
844
|
+
executeProviderPipeline = async (messages, query, temperature, modelOverride) => {
|
|
507
845
|
const chain = this.getProviderChain();
|
|
508
846
|
let lastError;
|
|
509
847
|
for (let index = 0; index < chain.length; index++) {
|
|
@@ -528,8 +866,9 @@ var AvantGateControlLayer = class {
|
|
|
528
866
|
}
|
|
529
867
|
}
|
|
530
868
|
throw lastError;
|
|
531
|
-
}
|
|
532
|
-
assembleResult(output) {
|
|
869
|
+
};
|
|
870
|
+
assembleResult = (output) => {
|
|
871
|
+
const sanitizedText = this.applyOutputSecurityGuards(output.responseText);
|
|
533
872
|
const costUSD = this.calculateCost(
|
|
534
873
|
output.modelUsed,
|
|
535
874
|
output.usage.promptTokens,
|
|
@@ -537,7 +876,7 @@ var AvantGateControlLayer = class {
|
|
|
537
876
|
output.usage.promptCacheHitTokens ?? 0
|
|
538
877
|
);
|
|
539
878
|
return {
|
|
540
|
-
text:
|
|
879
|
+
text: sanitizedText,
|
|
541
880
|
tokens: {
|
|
542
881
|
prompt: output.usage.promptTokens,
|
|
543
882
|
completion: output.usage.completionTokens,
|
|
@@ -548,8 +887,8 @@ var AvantGateControlLayer = class {
|
|
|
548
887
|
failoverOccurred: output.failoverOccurred,
|
|
549
888
|
attempts: output.attempts
|
|
550
889
|
};
|
|
551
|
-
}
|
|
552
|
-
async
|
|
890
|
+
};
|
|
891
|
+
notifyAuditSink = async (result) => {
|
|
553
892
|
if (!this.config.auditSink) {
|
|
554
893
|
return;
|
|
555
894
|
}
|
|
@@ -561,16 +900,16 @@ var AvantGateControlLayer = class {
|
|
|
561
900
|
failoverOccurred: result.failoverOccurred,
|
|
562
901
|
attempts: result.attempts
|
|
563
902
|
});
|
|
564
|
-
}
|
|
903
|
+
};
|
|
565
904
|
/**
|
|
566
905
|
* Exécute une requête avec garde d'entrée, masquage PII, garde pré-vol et calcul des coûts.
|
|
567
906
|
*/
|
|
568
|
-
async
|
|
907
|
+
execute = async (options) => {
|
|
569
908
|
const sanitizedQuery = this.applySecurityGuards(options.userQuery);
|
|
570
909
|
const messages = this.buildMessages(options.systemPrompt, sanitizedQuery);
|
|
571
910
|
const promptLength = (options.systemPrompt?.length ?? 0) + sanitizedQuery.length;
|
|
572
911
|
const estimatedPromptTokens = Math.ceil(promptLength / 4);
|
|
573
|
-
this.checkPreflightBudget(estimatedPromptTokens, this.config.primary.model, this.config.primary.provider);
|
|
912
|
+
await this.checkPreflightBudget(estimatedPromptTokens, this.config.primary.model, this.config.primary.provider);
|
|
574
913
|
if (!options.providerOverride && this.getProviderChain().length === 0) {
|
|
575
914
|
if (this.config.mockSimulation) {
|
|
576
915
|
const simResult = this.executeSimulation(sanitizedQuery, options.systemPrompt);
|
|
@@ -586,11 +925,11 @@ var AvantGateControlLayer = class {
|
|
|
586
925
|
this.checkPostExecutionBudget(result.tokens.total, result.costUSD);
|
|
587
926
|
await this.notifyAuditSink(result);
|
|
588
927
|
return result;
|
|
589
|
-
}
|
|
928
|
+
};
|
|
590
929
|
/**
|
|
591
930
|
* Exécute une requête et valide/répare le résultat selon un schéma Zod.
|
|
592
931
|
*/
|
|
593
|
-
async
|
|
932
|
+
executeStructured = async (options) => {
|
|
594
933
|
const rawResult = await this.execute({
|
|
595
934
|
userQuery: options.userQuery,
|
|
596
935
|
systemPrompt: options.systemPrompt,
|
|
@@ -612,12 +951,12 @@ var AvantGateControlLayer = class {
|
|
|
612
951
|
modelUsed: rawResult.modelUsed,
|
|
613
952
|
failoverOccurred: rawResult.failoverOccurred
|
|
614
953
|
};
|
|
615
|
-
}
|
|
954
|
+
};
|
|
616
955
|
/**
|
|
617
956
|
* Méthode unifiée de premier niveau pour l'extraction structurée sans code boilerplate.
|
|
618
957
|
* Gère le failover multi-fournisseurs, les retries, la validation Zod et la normalisation financière.
|
|
619
958
|
*/
|
|
620
|
-
async
|
|
959
|
+
generateStructuredOutput = async (options) => {
|
|
621
960
|
const maxRetries = options.maxRetries ?? this.config.retryOptions?.maxRetries ?? 2;
|
|
622
961
|
const modelToUse = options.model ?? this.config.primary.model;
|
|
623
962
|
const processedMessages = options.messages.map((msg) => {
|
|
@@ -628,7 +967,7 @@ var AvantGateControlLayer = class {
|
|
|
628
967
|
});
|
|
629
968
|
const promptLength = processedMessages.reduce((sum, msg) => sum + msg.content.length, 0);
|
|
630
969
|
const estimatedPromptTokens = Math.ceil(promptLength / 4);
|
|
631
|
-
this.checkPreflightBudget(estimatedPromptTokens, modelToUse);
|
|
970
|
+
await this.checkPreflightBudget(estimatedPromptTokens, modelToUse);
|
|
632
971
|
let lastError;
|
|
633
972
|
let accumulatedPromptTokens = 0;
|
|
634
973
|
let accumulatedCompletionTokens = 0;
|
|
@@ -676,10 +1015,11 @@ var AvantGateControlLayer = class {
|
|
|
676
1015
|
}
|
|
677
1016
|
accumulatedPromptTokens += attemptPromptTokens;
|
|
678
1017
|
accumulatedCompletionTokens += attemptCompletionTokens;
|
|
1018
|
+
const sanitizedResponse = this.applyOutputSecurityGuards(responseText);
|
|
679
1019
|
const isFinancial = options.financialNormalizer ?? Boolean(
|
|
680
1020
|
this.config.features?.finance?.enableFrenchAccounting || this.config.features?.finance
|
|
681
1021
|
);
|
|
682
|
-
const parsedData = validateWithZod(
|
|
1022
|
+
const parsedData = validateWithZod(sanitizedResponse, options.schema, {
|
|
683
1023
|
financialNormalizer: isFinancial,
|
|
684
1024
|
jurisdiction: this.config.features?.finance?.jurisdiction
|
|
685
1025
|
});
|
|
@@ -688,7 +1028,7 @@ var AvantGateControlLayer = class {
|
|
|
688
1028
|
this.checkPostExecutionBudget(totalTokens, costUSD);
|
|
689
1029
|
const result = {
|
|
690
1030
|
data: parsedData,
|
|
691
|
-
rawText:
|
|
1031
|
+
rawText: sanitizedResponse,
|
|
692
1032
|
tokens: {
|
|
693
1033
|
prompt: accumulatedPromptTokens,
|
|
694
1034
|
completion: accumulatedCompletionTokens,
|
|
@@ -716,11 +1056,11 @@ var AvantGateControlLayer = class {
|
|
|
716
1056
|
}
|
|
717
1057
|
}
|
|
718
1058
|
throw lastError;
|
|
719
|
-
}
|
|
1059
|
+
};
|
|
720
1060
|
};
|
|
721
|
-
|
|
1061
|
+
var createLLMControlLayer = (config) => {
|
|
722
1062
|
return new AvantGateControlLayer(config);
|
|
723
|
-
}
|
|
1063
|
+
};
|
|
724
1064
|
var createAvantGate = createLLMControlLayer;
|
|
725
1065
|
|
|
726
1066
|
// src/prompts/prompt-template.ts
|
|
@@ -1041,6 +1381,7 @@ export {
|
|
|
1041
1381
|
BudgetExceededError as AvantGateBudgetExceededError,
|
|
1042
1382
|
ConfigurationError as AvantGateConfigurationError,
|
|
1043
1383
|
AvantGateControlLayer,
|
|
1384
|
+
SecretLeakBlockedError as AvantGateSecretLeakBlockedError,
|
|
1044
1385
|
BudgetExceededError,
|
|
1045
1386
|
CachedPricingAdapter,
|
|
1046
1387
|
ConfigurationError,
|
|
@@ -1051,10 +1392,13 @@ export {
|
|
|
1051
1392
|
PromptRegistry,
|
|
1052
1393
|
PromptTemplate,
|
|
1053
1394
|
SEED_MODEL_PRICES,
|
|
1395
|
+
SecretLeakBlockedError,
|
|
1396
|
+
SqlitePricingAdapter,
|
|
1054
1397
|
WorkflowAbortSignal,
|
|
1055
1398
|
WorkflowSagaRollbackError,
|
|
1056
1399
|
WorkflowValidationError,
|
|
1057
1400
|
AvantGateControlLayer as ZenLLMControlLayer,
|
|
1401
|
+
applyOutputGuards,
|
|
1058
1402
|
calculateCostUSD,
|
|
1059
1403
|
createAvantGate,
|
|
1060
1404
|
createHttpProviderClient,
|
|
@@ -1066,6 +1410,7 @@ export {
|
|
|
1066
1410
|
resolveModelPrice,
|
|
1067
1411
|
resolveModelPriceAsync,
|
|
1068
1412
|
sanitizePII,
|
|
1413
|
+
sanitizeSecrets,
|
|
1069
1414
|
validateUserInput,
|
|
1070
1415
|
validateWithZod
|
|
1071
1416
|
};
|