avantgate 1.6.1 → 1.7.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 +15 -1
- 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 +7 -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 +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +421 -130
- package/dist/index.mjs +318 -103
- 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/sqlite-pricing-adapter.d.ts +87 -0
- package/dist/sqlite-pricing-adapter.d.ts.map +1 -0
- package/dist/workflow/index.js +92 -24
- package/dist/workflow/index.mjs +3 -2
- package/package.json +4 -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 {
|
|
@@ -28,6 +31,213 @@ var BudgetExceededError = class extends Error {
|
|
|
28
31
|
}
|
|
29
32
|
};
|
|
30
33
|
|
|
34
|
+
// src/sqlite-pricing-adapter.ts
|
|
35
|
+
import fs from "fs";
|
|
36
|
+
import path from "path";
|
|
37
|
+
var normalizeIdentifier = (val) => {
|
|
38
|
+
return val.trim().toLowerCase().replace(":", "/");
|
|
39
|
+
};
|
|
40
|
+
var SqlitePricingAdapter = class {
|
|
41
|
+
db;
|
|
42
|
+
tableName;
|
|
43
|
+
constructor(options = {}) {
|
|
44
|
+
this.tableName = options.tableName ?? "model_pricing";
|
|
45
|
+
this.db = options.db ?? this.initDatabase(options.dbPath ?? ":memory:");
|
|
46
|
+
this.initSchema();
|
|
47
|
+
if (options.autoSeed) {
|
|
48
|
+
this.seedDefaultPrices(false);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
initDatabase = (dbPath) => {
|
|
52
|
+
if (dbPath !== ":memory:") {
|
|
53
|
+
const dir = path.dirname(path.resolve(dbPath));
|
|
54
|
+
if (!fs.existsSync(dir)) {
|
|
55
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const { DatabaseSync } = __require("sqlite");
|
|
60
|
+
return new DatabaseSync(dbPath);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
throw new ConfigurationError(
|
|
63
|
+
`[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)}`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
initSchema = () => {
|
|
68
|
+
this.db.exec(`
|
|
69
|
+
CREATE TABLE IF NOT EXISTS ${this.tableName} (
|
|
70
|
+
provider TEXT NOT NULL,
|
|
71
|
+
model TEXT NOT NULL,
|
|
72
|
+
prompt_usd_per_million REAL NOT NULL,
|
|
73
|
+
completion_usd_per_million REAL NOT NULL,
|
|
74
|
+
cache_hit_usd_per_million REAL,
|
|
75
|
+
is_active INTEGER DEFAULT 1,
|
|
76
|
+
updated_at TEXT NOT NULL,
|
|
77
|
+
PRIMARY KEY (provider, model)
|
|
78
|
+
);
|
|
79
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tableName}_lookup
|
|
80
|
+
ON ${this.tableName}(provider, model, is_active);
|
|
81
|
+
`);
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Implémentation du port avantGate PricingAdapter.
|
|
85
|
+
* Résolution hiérarchique : (provider, model) -> (provider/model) -> (model).
|
|
86
|
+
*/
|
|
87
|
+
fetchPrice = (model, provider) => {
|
|
88
|
+
const normModel = normalizeIdentifier(model);
|
|
89
|
+
const normProvider = provider ? normalizeIdentifier(provider) : void 0;
|
|
90
|
+
if (normProvider) {
|
|
91
|
+
const row = this.queryActiveRow(normProvider, normModel);
|
|
92
|
+
if (row) return this.mapRowToPrice(row);
|
|
93
|
+
}
|
|
94
|
+
if (normModel.includes("/")) {
|
|
95
|
+
const firstSlashIdx = normModel.indexOf("/");
|
|
96
|
+
const extractedProvider = normModel.slice(0, firstSlashIdx);
|
|
97
|
+
const extractedModel = normModel.slice(firstSlashIdx + 1);
|
|
98
|
+
const splitRow = this.queryActiveRow(extractedProvider, extractedModel);
|
|
99
|
+
if (splitRow) return this.mapRowToPrice(splitRow);
|
|
100
|
+
}
|
|
101
|
+
const directRow = this.queryByModelOnly(normModel);
|
|
102
|
+
if (directRow) return this.mapRowToPrice(directRow);
|
|
103
|
+
return void 0;
|
|
104
|
+
};
|
|
105
|
+
queryActiveRow = (provider, model) => {
|
|
106
|
+
const stmt = this.db.prepare(`
|
|
107
|
+
SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
|
|
108
|
+
FROM ${this.tableName}
|
|
109
|
+
WHERE provider = ? AND model = ? AND is_active = 1
|
|
110
|
+
LIMIT 1
|
|
111
|
+
`);
|
|
112
|
+
return stmt.get(provider, model);
|
|
113
|
+
};
|
|
114
|
+
queryByModelOnly = (model) => {
|
|
115
|
+
const stmt = this.db.prepare(`
|
|
116
|
+
SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
|
|
117
|
+
FROM ${this.tableName}
|
|
118
|
+
WHERE model = ? AND is_active = 1
|
|
119
|
+
LIMIT 1
|
|
120
|
+
`);
|
|
121
|
+
return stmt.get(model);
|
|
122
|
+
};
|
|
123
|
+
mapRowToPrice = (row) => {
|
|
124
|
+
return {
|
|
125
|
+
promptUSDPerMillion: Number(row.prompt_usd_per_million),
|
|
126
|
+
completionUSDPerMillion: Number(row.completion_usd_per_million),
|
|
127
|
+
cacheHitUSDPerMillion: row.cache_hit_usd_per_million != null ? Number(row.cache_hit_usd_per_million) : void 0
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Enregistre ou met à jour la tarification d'un modèle.
|
|
132
|
+
*/
|
|
133
|
+
upsertPrice = (provider, model, price, isActive = true) => {
|
|
134
|
+
const normProvider = normalizeIdentifier(provider);
|
|
135
|
+
const normModel = normalizeIdentifier(model);
|
|
136
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
137
|
+
const stmt = this.db.prepare(`
|
|
138
|
+
INSERT OR REPLACE INTO ${this.tableName}
|
|
139
|
+
(provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at)
|
|
140
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
141
|
+
`);
|
|
142
|
+
stmt.run(
|
|
143
|
+
normProvider,
|
|
144
|
+
normModel,
|
|
145
|
+
price.promptUSDPerMillion,
|
|
146
|
+
price.completionUSDPerMillion,
|
|
147
|
+
price.cacheHitUSDPerMillion ?? null,
|
|
148
|
+
isActive ? 1 : 0,
|
|
149
|
+
now
|
|
150
|
+
);
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Supprime une règle tarifaire de la base de données.
|
|
154
|
+
*/
|
|
155
|
+
deletePrice = (provider, model) => {
|
|
156
|
+
const normProvider = normalizeIdentifier(provider);
|
|
157
|
+
const normModel = normalizeIdentifier(model);
|
|
158
|
+
const stmt = this.db.prepare(`
|
|
159
|
+
DELETE FROM ${this.tableName}
|
|
160
|
+
WHERE provider = ? AND model = ?
|
|
161
|
+
`);
|
|
162
|
+
const result = stmt.run(normProvider, normModel);
|
|
163
|
+
return Boolean(result && typeof result.changes === "number" && result.changes > 0);
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Retourne l'ensemble des règles tarifaires enregistrées.
|
|
167
|
+
*/
|
|
168
|
+
getAllPrices = () => {
|
|
169
|
+
const stmt = this.db.prepare(`
|
|
170
|
+
SELECT provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at
|
|
171
|
+
FROM ${this.tableName}
|
|
172
|
+
ORDER BY provider ASC, model ASC
|
|
173
|
+
`);
|
|
174
|
+
const rows = stmt.all();
|
|
175
|
+
return rows.map((row) => ({
|
|
176
|
+
provider: row.provider,
|
|
177
|
+
model: row.model,
|
|
178
|
+
price: this.mapRowToPrice(row),
|
|
179
|
+
isActive: Boolean(row.is_active),
|
|
180
|
+
updatedAt: row.updated_at
|
|
181
|
+
}));
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* Remplit la base avec le catalogue de départ SEED_MODEL_PRICES.
|
|
185
|
+
* @param force Si true, écrase les tarifs existants. Si false, insère uniquement les modèles manquants.
|
|
186
|
+
* @returns Le nombre d'enregistrements insérés.
|
|
187
|
+
*/
|
|
188
|
+
seedDefaultPrices = (force = false) => {
|
|
189
|
+
const conflictClause = force ? "INSERT OR REPLACE" : "INSERT OR IGNORE";
|
|
190
|
+
const stmt = this.db.prepare(`
|
|
191
|
+
${conflictClause} INTO ${this.tableName}
|
|
192
|
+
(provider, model, prompt_usd_per_million, completion_usd_per_million, cache_hit_usd_per_million, is_active, updated_at)
|
|
193
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
194
|
+
`);
|
|
195
|
+
let inserted = 0;
|
|
196
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
197
|
+
for (const [key, price] of Object.entries(SEED_MODEL_PRICES)) {
|
|
198
|
+
const normKey = normalizeIdentifier(key);
|
|
199
|
+
let provider = "direct";
|
|
200
|
+
let model = normKey;
|
|
201
|
+
if (normKey.includes("/")) {
|
|
202
|
+
const slashIdx = normKey.indexOf("/");
|
|
203
|
+
provider = normKey.slice(0, slashIdx);
|
|
204
|
+
model = normKey.slice(slashIdx + 1);
|
|
205
|
+
}
|
|
206
|
+
const res1 = stmt.run(
|
|
207
|
+
provider,
|
|
208
|
+
model,
|
|
209
|
+
price.promptUSDPerMillion,
|
|
210
|
+
price.completionUSDPerMillion,
|
|
211
|
+
price.cacheHitUSDPerMillion ?? null,
|
|
212
|
+
1,
|
|
213
|
+
now
|
|
214
|
+
);
|
|
215
|
+
if (res1?.changes && res1.changes > 0) inserted++;
|
|
216
|
+
if (provider !== "direct") {
|
|
217
|
+
const res2 = stmt.run(
|
|
218
|
+
"direct",
|
|
219
|
+
normKey,
|
|
220
|
+
price.promptUSDPerMillion,
|
|
221
|
+
price.completionUSDPerMillion,
|
|
222
|
+
price.cacheHitUSDPerMillion ?? null,
|
|
223
|
+
1,
|
|
224
|
+
now
|
|
225
|
+
);
|
|
226
|
+
if (res2?.changes && res2.changes > 0) inserted++;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return inserted;
|
|
230
|
+
};
|
|
231
|
+
/**
|
|
232
|
+
* Ferme la connexion SQLite sous-jacente si supporté.
|
|
233
|
+
*/
|
|
234
|
+
close = () => {
|
|
235
|
+
if (typeof this.db.close === "function") {
|
|
236
|
+
this.db.close();
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
};
|
|
240
|
+
|
|
31
241
|
// src/pricing.ts
|
|
32
242
|
var SEED_MODEL_PRICES = {
|
|
33
243
|
// DeepSeek Direct
|
|
@@ -52,9 +262,9 @@ var SEED_MODEL_PRICES = {
|
|
|
52
262
|
// Local Ollama (Toujours $0)
|
|
53
263
|
"ollama": { promptUSDPerMillion: 0, completionUSDPerMillion: 0 }
|
|
54
264
|
};
|
|
55
|
-
|
|
265
|
+
var normalizeKey = (identifier) => {
|
|
56
266
|
return identifier.trim().toLowerCase().replace(":", "/");
|
|
57
|
-
}
|
|
267
|
+
};
|
|
58
268
|
var CachedPricingAdapter = class {
|
|
59
269
|
cache = /* @__PURE__ */ new Map();
|
|
60
270
|
ttlMs;
|
|
@@ -63,7 +273,7 @@ var CachedPricingAdapter = class {
|
|
|
63
273
|
this.delegate = delegate;
|
|
64
274
|
this.ttlMs = ttlMs;
|
|
65
275
|
}
|
|
66
|
-
async
|
|
276
|
+
fetchPrice = async (model, provider) => {
|
|
67
277
|
const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
|
|
68
278
|
const cached = this.cache.get(key);
|
|
69
279
|
if (cached && Date.now() < cached.expiresAt) {
|
|
@@ -81,72 +291,76 @@ var CachedPricingAdapter = class {
|
|
|
81
291
|
}
|
|
82
292
|
}
|
|
83
293
|
return void 0;
|
|
84
|
-
}
|
|
85
|
-
peek(model, provider) {
|
|
294
|
+
};
|
|
295
|
+
peek = (model, provider) => {
|
|
86
296
|
const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
297
|
+
const cached = this.cache.get(key);
|
|
298
|
+
if (cached && Date.now() < cached.expiresAt) {
|
|
299
|
+
return cached.price;
|
|
300
|
+
}
|
|
301
|
+
return void 0;
|
|
302
|
+
};
|
|
303
|
+
clearCache = () => {
|
|
90
304
|
this.cache.clear();
|
|
91
|
-
}
|
|
92
|
-
invalidate(model, provider) {
|
|
305
|
+
};
|
|
306
|
+
invalidate = (model, provider) => {
|
|
93
307
|
if (!model) {
|
|
94
308
|
this.clearCache();
|
|
95
309
|
return;
|
|
96
310
|
}
|
|
97
311
|
const key = provider ? normalizeKey(`${provider}/${model}`) : normalizeKey(model);
|
|
98
312
|
this.cache.delete(key);
|
|
99
|
-
}
|
|
313
|
+
};
|
|
100
314
|
};
|
|
101
|
-
var PricingRegistry = class {
|
|
315
|
+
var PricingRegistry = class _PricingRegistry {
|
|
102
316
|
static prices = /* @__PURE__ */ new Map();
|
|
103
317
|
static adapter;
|
|
104
|
-
static registerPrice(identifier, price) {
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
static registerDistributorPrices(distributor, priceMap) {
|
|
318
|
+
static registerPrice = (identifier, price) => {
|
|
319
|
+
_PricingRegistry.prices.set(normalizeKey(identifier), price);
|
|
320
|
+
};
|
|
321
|
+
static registerDistributorPrices = (distributor, priceMap) => {
|
|
108
322
|
for (const [model, price] of Object.entries(priceMap)) {
|
|
109
|
-
|
|
323
|
+
_PricingRegistry.prices.set(normalizeKey(`${distributor}/${model}`), price);
|
|
110
324
|
}
|
|
111
|
-
}
|
|
112
|
-
static registerPrices(prices) {
|
|
325
|
+
};
|
|
326
|
+
static registerPrices = (prices) => {
|
|
113
327
|
for (const [key, price] of Object.entries(prices)) {
|
|
114
|
-
|
|
328
|
+
_PricingRegistry.prices.set(normalizeKey(key), price);
|
|
115
329
|
}
|
|
116
|
-
}
|
|
117
|
-
static getPrice(model, provider) {
|
|
330
|
+
};
|
|
331
|
+
static getPrice = (model, provider) => {
|
|
118
332
|
if (provider) {
|
|
119
333
|
const distributorKey = normalizeKey(`${provider}/${model}`);
|
|
120
|
-
const directDistributorPrice =
|
|
334
|
+
const directDistributorPrice = _PricingRegistry.prices.get(distributorKey);
|
|
121
335
|
if (directDistributorPrice) {
|
|
122
336
|
return directDistributorPrice;
|
|
123
337
|
}
|
|
124
338
|
}
|
|
125
|
-
return
|
|
126
|
-
}
|
|
127
|
-
static setAdapter(adapter) {
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
static getAdapter() {
|
|
131
|
-
return
|
|
132
|
-
}
|
|
133
|
-
static clear() {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
339
|
+
return _PricingRegistry.prices.get(normalizeKey(model));
|
|
340
|
+
};
|
|
341
|
+
static setAdapter = (adapter) => {
|
|
342
|
+
_PricingRegistry.adapter = adapter;
|
|
343
|
+
};
|
|
344
|
+
static getAdapter = () => {
|
|
345
|
+
return _PricingRegistry.adapter;
|
|
346
|
+
};
|
|
347
|
+
static clear = () => {
|
|
348
|
+
_PricingRegistry.prices.clear();
|
|
349
|
+
_PricingRegistry.adapter = void 0;
|
|
350
|
+
};
|
|
137
351
|
/**
|
|
138
352
|
* Réinitialise le registre en chargeant le catalogue d'exemple SEED_MODEL_PRICES.
|
|
139
353
|
*/
|
|
140
|
-
static loadSeedPrices() {
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
static getAllPrices() {
|
|
354
|
+
static loadSeedPrices = () => {
|
|
355
|
+
_PricingRegistry.registerPrices(SEED_MODEL_PRICES);
|
|
356
|
+
};
|
|
357
|
+
static getAllPrices = () => {
|
|
144
358
|
const out = {};
|
|
145
|
-
for (const [k, v] of
|
|
359
|
+
for (const [k, v] of _PricingRegistry.prices.entries()) {
|
|
146
360
|
out[k] = v;
|
|
147
361
|
}
|
|
148
362
|
return out;
|
|
149
|
-
}
|
|
363
|
+
};
|
|
150
364
|
};
|
|
151
365
|
var DEFAULT_MODEL_PRICES = new Proxy({}, {
|
|
152
366
|
get(_target, prop) {
|
|
@@ -166,7 +380,7 @@ var DEFAULT_MODEL_PRICES = new Proxy({}, {
|
|
|
166
380
|
return void 0;
|
|
167
381
|
}
|
|
168
382
|
});
|
|
169
|
-
|
|
383
|
+
var resolveModelPrice = (model, options) => {
|
|
170
384
|
const opts = typeof options === "string" ? { provider: options } : options ?? {};
|
|
171
385
|
if (opts.providerPricing) {
|
|
172
386
|
return opts.providerPricing;
|
|
@@ -197,8 +411,8 @@ function resolveModelPrice(model, options) {
|
|
|
197
411
|
return { promptUSDPerMillion: 0, completionUSDPerMillion: 0 };
|
|
198
412
|
}
|
|
199
413
|
return void 0;
|
|
200
|
-
}
|
|
201
|
-
async
|
|
414
|
+
};
|
|
415
|
+
var resolveModelPriceAsync = async (model, options) => {
|
|
202
416
|
const opts = typeof options === "string" ? { provider: options } : options ?? {};
|
|
203
417
|
const activeAdapter = opts.adapter ?? PricingRegistry.getAdapter();
|
|
204
418
|
if (activeAdapter) {
|
|
@@ -211,8 +425,8 @@ async function resolveModelPriceAsync(model, options) {
|
|
|
211
425
|
}
|
|
212
426
|
}
|
|
213
427
|
return resolveModelPrice(model, options);
|
|
214
|
-
}
|
|
215
|
-
|
|
428
|
+
};
|
|
429
|
+
var calculateCostUSD = (model, promptTokens, completionTokens, cacheHitTokens = 0, options) => {
|
|
216
430
|
const pricing = resolveModelPrice(model, options);
|
|
217
431
|
if (!pricing) {
|
|
218
432
|
return 0;
|
|
@@ -221,7 +435,7 @@ function calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens
|
|
|
221
435
|
const completionCost = completionTokens / 1e6 * pricing.completionUSDPerMillion;
|
|
222
436
|
const cacheDiscount = cacheHitTokens && pricing.cacheHitUSDPerMillion ? cacheHitTokens / 1e6 * (pricing.promptUSDPerMillion - pricing.cacheHitUSDPerMillion) : 0;
|
|
223
437
|
return Math.max(0, promptCost + completionCost - cacheDiscount);
|
|
224
|
-
}
|
|
438
|
+
};
|
|
225
439
|
|
|
226
440
|
// src/input-guard.ts
|
|
227
441
|
var INJECTION_PATTERNS = [
|
|
@@ -232,7 +446,7 @@ var INJECTION_PATTERNS = [
|
|
|
232
446
|
/output\s+(?:your\s+)?(?:system\s+prompt|initial\s+instructions)/i,
|
|
233
447
|
/repeat\s+(?:the\s+words\s+above|everything\s+above)/i
|
|
234
448
|
];
|
|
235
|
-
|
|
449
|
+
var validateUserInput = (input, options) => {
|
|
236
450
|
const maxLength = options?.maxLength ?? 5e4;
|
|
237
451
|
if (input.length > maxLength) {
|
|
238
452
|
return {
|
|
@@ -251,7 +465,7 @@ function validateUserInput(input, options) {
|
|
|
251
465
|
}
|
|
252
466
|
}
|
|
253
467
|
return { valid: true };
|
|
254
|
-
}
|
|
468
|
+
};
|
|
255
469
|
|
|
256
470
|
// src/providers/http-client.ts
|
|
257
471
|
var DEFAULT_BASE_URLS = {
|
|
@@ -272,14 +486,14 @@ var HttpProviderClient = class {
|
|
|
272
486
|
this.apiKey = config.apiKey;
|
|
273
487
|
this.baseUrl = config.baseUrl || DEFAULT_BASE_URLS[config.provider] || "https://api.openai.com/v1";
|
|
274
488
|
}
|
|
275
|
-
resolveEndpoint() {
|
|
489
|
+
resolveEndpoint = () => {
|
|
276
490
|
const trimmed = this.baseUrl.replace(/\/+$/, "");
|
|
277
491
|
if (trimmed.endsWith("/chat/completions")) {
|
|
278
492
|
return trimmed;
|
|
279
493
|
}
|
|
280
494
|
return `${trimmed}/chat/completions`;
|
|
281
|
-
}
|
|
282
|
-
buildHeaders() {
|
|
495
|
+
};
|
|
496
|
+
buildHeaders = () => {
|
|
283
497
|
const headers = {
|
|
284
498
|
"Content-Type": "application/json"
|
|
285
499
|
};
|
|
@@ -291,8 +505,8 @@ var HttpProviderClient = class {
|
|
|
291
505
|
headers["X-Title"] = "AvantGate";
|
|
292
506
|
}
|
|
293
507
|
return headers;
|
|
294
|
-
}
|
|
295
|
-
buildPayload(options, model) {
|
|
508
|
+
};
|
|
509
|
+
buildPayload = (options, model) => {
|
|
296
510
|
return {
|
|
297
511
|
model,
|
|
298
512
|
messages: options.messages.map((m) => ({
|
|
@@ -302,8 +516,8 @@ var HttpProviderClient = class {
|
|
|
302
516
|
temperature: options.temperature ?? 0.2,
|
|
303
517
|
...options.responseFormat ? { response_format: options.responseFormat } : {}
|
|
304
518
|
};
|
|
305
|
-
}
|
|
306
|
-
extractUsage(usageData) {
|
|
519
|
+
};
|
|
520
|
+
extractUsage = (usageData) => {
|
|
307
521
|
if (!usageData) {
|
|
308
522
|
return void 0;
|
|
309
523
|
}
|
|
@@ -313,8 +527,8 @@ var HttpProviderClient = class {
|
|
|
313
527
|
totalTokens: usageData.total_tokens,
|
|
314
528
|
promptCacheHitTokens: usageData.prompt_tokens_details?.cached_tokens
|
|
315
529
|
};
|
|
316
|
-
}
|
|
317
|
-
async
|
|
530
|
+
};
|
|
531
|
+
complete = async (options) => {
|
|
318
532
|
const endpoint = this.resolveEndpoint();
|
|
319
533
|
const model = options.model ?? "default";
|
|
320
534
|
const headers = this.buildHeaders();
|
|
@@ -334,11 +548,11 @@ var HttpProviderClient = class {
|
|
|
334
548
|
const text = data.choices?.[0]?.message?.content ?? "";
|
|
335
549
|
const usage = this.extractUsage(data.usage);
|
|
336
550
|
return { text, usage };
|
|
337
|
-
}
|
|
551
|
+
};
|
|
338
552
|
};
|
|
339
|
-
|
|
553
|
+
var createHttpProviderClient = (config) => {
|
|
340
554
|
return new HttpProviderClient(config);
|
|
341
|
-
}
|
|
555
|
+
};
|
|
342
556
|
|
|
343
557
|
// src/control-layer.ts
|
|
344
558
|
var AvantGateControlLayer = class {
|
|
@@ -358,7 +572,7 @@ var AvantGateControlLayer = class {
|
|
|
358
572
|
);
|
|
359
573
|
}
|
|
360
574
|
}
|
|
361
|
-
resolveProviderConfig(provider) {
|
|
575
|
+
resolveProviderConfig = (provider) => {
|
|
362
576
|
if (!provider) return void 0;
|
|
363
577
|
if (provider.client) return provider;
|
|
364
578
|
if (provider.apiKey || provider.baseUrl || provider.provider === "ollama") {
|
|
@@ -368,8 +582,8 @@ var AvantGateControlLayer = class {
|
|
|
368
582
|
};
|
|
369
583
|
}
|
|
370
584
|
return provider;
|
|
371
|
-
}
|
|
372
|
-
findProviderConfig(provider, model) {
|
|
585
|
+
};
|
|
586
|
+
findProviderConfig = (provider, model) => {
|
|
373
587
|
const list = [this.config.primary, this.config.fallback, this.config.emergencyFallback].filter(
|
|
374
588
|
(p) => Boolean(p)
|
|
375
589
|
);
|
|
@@ -382,8 +596,8 @@ var AvantGateControlLayer = class {
|
|
|
382
596
|
if (matchModel) return matchModel;
|
|
383
597
|
}
|
|
384
598
|
return this.config.primary;
|
|
385
|
-
}
|
|
386
|
-
calculateCost(model, promptTokens, completionTokens, cacheHitTokens = 0, provider) {
|
|
599
|
+
};
|
|
600
|
+
calculateCost = (model, promptTokens, completionTokens, cacheHitTokens = 0, provider) => {
|
|
387
601
|
const providerCfg = this.findProviderConfig(provider, model);
|
|
388
602
|
return calculateCostUSD(model, promptTokens, completionTokens, cacheHitTokens, {
|
|
389
603
|
provider: provider ?? providerCfg?.provider,
|
|
@@ -391,8 +605,8 @@ var AvantGateControlLayer = class {
|
|
|
391
605
|
customPricing: this.config.customPricing,
|
|
392
606
|
adapter: this.cachedPricingAdapter
|
|
393
607
|
});
|
|
394
|
-
}
|
|
395
|
-
applySecurityGuards(userQuery) {
|
|
608
|
+
};
|
|
609
|
+
applySecurityGuards = (userQuery) => {
|
|
396
610
|
const guard = validateUserInput(userQuery, {
|
|
397
611
|
detectInjection: this.config.security?.detectPromptInjection,
|
|
398
612
|
maxLength: this.config.security?.maxInputLength
|
|
@@ -404,8 +618,8 @@ var AvantGateControlLayer = class {
|
|
|
404
618
|
return sanitizePII(userQuery).text;
|
|
405
619
|
}
|
|
406
620
|
return userQuery;
|
|
407
|
-
}
|
|
408
|
-
checkPreflightBudget(estimatedPromptTokens, targetModel, provider) {
|
|
621
|
+
};
|
|
622
|
+
checkPreflightBudget = async (estimatedPromptTokens, targetModel, provider) => {
|
|
409
623
|
if (this.config.maxTokenBudget !== void 0 && estimatedPromptTokens > this.config.maxTokenBudget) {
|
|
410
624
|
throw new BudgetExceededError(
|
|
411
625
|
`[AvantGate Budget Guard] Pre-flight token budget exceeded: estimated prompt (${estimatedPromptTokens} tokens) exceeds maxTokenBudget (${this.config.maxTokenBudget}).`
|
|
@@ -414,7 +628,7 @@ var AvantGateControlLayer = class {
|
|
|
414
628
|
if (this.config.maxCostUSD !== void 0) {
|
|
415
629
|
const isLocalFree = provider === "ollama" || targetModel.toLowerCase().includes("ollama");
|
|
416
630
|
const providerCfg = this.findProviderConfig(provider, targetModel);
|
|
417
|
-
const price =
|
|
631
|
+
const price = await resolveModelPriceAsync(targetModel, {
|
|
418
632
|
provider: provider ?? providerCfg?.provider,
|
|
419
633
|
providerPricing: providerCfg?.pricing,
|
|
420
634
|
customPricing: this.config.customPricing,
|
|
@@ -432,8 +646,8 @@ var AvantGateControlLayer = class {
|
|
|
432
646
|
);
|
|
433
647
|
}
|
|
434
648
|
}
|
|
435
|
-
}
|
|
436
|
-
checkPostExecutionBudget(tokensTotal, costUSD) {
|
|
649
|
+
};
|
|
650
|
+
checkPostExecutionBudget = (tokensTotal, costUSD) => {
|
|
437
651
|
if (this.config.maxTokenBudget !== void 0 && tokensTotal > this.config.maxTokenBudget) {
|
|
438
652
|
throw new BudgetExceededError(
|
|
439
653
|
`[AvantGate Budget Guard] Execution total tokens (${tokensTotal}) exceeded maxTokenBudget (${this.config.maxTokenBudget}).`
|
|
@@ -444,16 +658,16 @@ var AvantGateControlLayer = class {
|
|
|
444
658
|
`[AvantGate Budget Guard] Execution cost ($${costUSD.toFixed(6)}) exceeded maxCostUSD ($${this.config.maxCostUSD}).`
|
|
445
659
|
);
|
|
446
660
|
}
|
|
447
|
-
}
|
|
448
|
-
buildMessages(systemPrompt, query) {
|
|
661
|
+
};
|
|
662
|
+
buildMessages = (systemPrompt, query) => {
|
|
449
663
|
const messages = [];
|
|
450
664
|
if (systemPrompt) {
|
|
451
665
|
messages.push({ role: "system", content: systemPrompt });
|
|
452
666
|
}
|
|
453
667
|
messages.push({ role: "user", content: query });
|
|
454
668
|
return messages;
|
|
455
|
-
}
|
|
456
|
-
resolveTokens(rawUsage, query, text) {
|
|
669
|
+
};
|
|
670
|
+
resolveTokens = (rawUsage, query, text) => {
|
|
457
671
|
const promptTokens = rawUsage?.promptTokens ?? Math.ceil(query.length / 4);
|
|
458
672
|
const completionTokens = rawUsage?.completionTokens ?? Math.ceil(text.length / 4);
|
|
459
673
|
const totalTokens = rawUsage?.totalTokens ?? promptTokens + completionTokens;
|
|
@@ -463,15 +677,15 @@ var AvantGateControlLayer = class {
|
|
|
463
677
|
totalTokens,
|
|
464
678
|
promptCacheHitTokens: rawUsage?.promptCacheHitTokens
|
|
465
679
|
};
|
|
466
|
-
}
|
|
467
|
-
getProviderChain() {
|
|
680
|
+
};
|
|
681
|
+
getProviderChain = () => {
|
|
468
682
|
return [
|
|
469
683
|
this.config.primary,
|
|
470
684
|
this.config.fallback,
|
|
471
685
|
this.config.emergencyFallback
|
|
472
686
|
].filter((provider) => Boolean(provider?.client));
|
|
473
|
-
}
|
|
474
|
-
executeSimulation(query, systemPrompt) {
|
|
687
|
+
};
|
|
688
|
+
executeSimulation = (query, systemPrompt) => {
|
|
475
689
|
const promptTokens = Math.ceil(((systemPrompt?.length ?? 0) + query.length) / 4);
|
|
476
690
|
const completionTokens = 50;
|
|
477
691
|
const cost = this.calculateCost(this.config.primary.model, promptTokens, completionTokens);
|
|
@@ -487,8 +701,8 @@ var AvantGateControlLayer = class {
|
|
|
487
701
|
failoverOccurred: false,
|
|
488
702
|
attempts: 1
|
|
489
703
|
};
|
|
490
|
-
}
|
|
491
|
-
async
|
|
704
|
+
};
|
|
705
|
+
executeOverrideProvider = async (provider, messages, query, temperature) => {
|
|
492
706
|
const response = await provider.complete({
|
|
493
707
|
model: this.config.primary.model,
|
|
494
708
|
messages,
|
|
@@ -502,8 +716,8 @@ var AvantGateControlLayer = class {
|
|
|
502
716
|
failoverOccurred: false,
|
|
503
717
|
attempts: 1
|
|
504
718
|
};
|
|
505
|
-
}
|
|
506
|
-
async
|
|
719
|
+
};
|
|
720
|
+
executeProviderPipeline = async (messages, query, temperature, modelOverride) => {
|
|
507
721
|
const chain = this.getProviderChain();
|
|
508
722
|
let lastError;
|
|
509
723
|
for (let index = 0; index < chain.length; index++) {
|
|
@@ -528,8 +742,8 @@ var AvantGateControlLayer = class {
|
|
|
528
742
|
}
|
|
529
743
|
}
|
|
530
744
|
throw lastError;
|
|
531
|
-
}
|
|
532
|
-
assembleResult(output) {
|
|
745
|
+
};
|
|
746
|
+
assembleResult = (output) => {
|
|
533
747
|
const costUSD = this.calculateCost(
|
|
534
748
|
output.modelUsed,
|
|
535
749
|
output.usage.promptTokens,
|
|
@@ -548,8 +762,8 @@ var AvantGateControlLayer = class {
|
|
|
548
762
|
failoverOccurred: output.failoverOccurred,
|
|
549
763
|
attempts: output.attempts
|
|
550
764
|
};
|
|
551
|
-
}
|
|
552
|
-
async
|
|
765
|
+
};
|
|
766
|
+
notifyAuditSink = async (result) => {
|
|
553
767
|
if (!this.config.auditSink) {
|
|
554
768
|
return;
|
|
555
769
|
}
|
|
@@ -561,16 +775,16 @@ var AvantGateControlLayer = class {
|
|
|
561
775
|
failoverOccurred: result.failoverOccurred,
|
|
562
776
|
attempts: result.attempts
|
|
563
777
|
});
|
|
564
|
-
}
|
|
778
|
+
};
|
|
565
779
|
/**
|
|
566
780
|
* Exécute une requête avec garde d'entrée, masquage PII, garde pré-vol et calcul des coûts.
|
|
567
781
|
*/
|
|
568
|
-
async
|
|
782
|
+
execute = async (options) => {
|
|
569
783
|
const sanitizedQuery = this.applySecurityGuards(options.userQuery);
|
|
570
784
|
const messages = this.buildMessages(options.systemPrompt, sanitizedQuery);
|
|
571
785
|
const promptLength = (options.systemPrompt?.length ?? 0) + sanitizedQuery.length;
|
|
572
786
|
const estimatedPromptTokens = Math.ceil(promptLength / 4);
|
|
573
|
-
this.checkPreflightBudget(estimatedPromptTokens, this.config.primary.model, this.config.primary.provider);
|
|
787
|
+
await this.checkPreflightBudget(estimatedPromptTokens, this.config.primary.model, this.config.primary.provider);
|
|
574
788
|
if (!options.providerOverride && this.getProviderChain().length === 0) {
|
|
575
789
|
if (this.config.mockSimulation) {
|
|
576
790
|
const simResult = this.executeSimulation(sanitizedQuery, options.systemPrompt);
|
|
@@ -586,11 +800,11 @@ var AvantGateControlLayer = class {
|
|
|
586
800
|
this.checkPostExecutionBudget(result.tokens.total, result.costUSD);
|
|
587
801
|
await this.notifyAuditSink(result);
|
|
588
802
|
return result;
|
|
589
|
-
}
|
|
803
|
+
};
|
|
590
804
|
/**
|
|
591
805
|
* Exécute une requête et valide/répare le résultat selon un schéma Zod.
|
|
592
806
|
*/
|
|
593
|
-
async
|
|
807
|
+
executeStructured = async (options) => {
|
|
594
808
|
const rawResult = await this.execute({
|
|
595
809
|
userQuery: options.userQuery,
|
|
596
810
|
systemPrompt: options.systemPrompt,
|
|
@@ -612,12 +826,12 @@ var AvantGateControlLayer = class {
|
|
|
612
826
|
modelUsed: rawResult.modelUsed,
|
|
613
827
|
failoverOccurred: rawResult.failoverOccurred
|
|
614
828
|
};
|
|
615
|
-
}
|
|
829
|
+
};
|
|
616
830
|
/**
|
|
617
831
|
* Méthode unifiée de premier niveau pour l'extraction structurée sans code boilerplate.
|
|
618
832
|
* Gère le failover multi-fournisseurs, les retries, la validation Zod et la normalisation financière.
|
|
619
833
|
*/
|
|
620
|
-
async
|
|
834
|
+
generateStructuredOutput = async (options) => {
|
|
621
835
|
const maxRetries = options.maxRetries ?? this.config.retryOptions?.maxRetries ?? 2;
|
|
622
836
|
const modelToUse = options.model ?? this.config.primary.model;
|
|
623
837
|
const processedMessages = options.messages.map((msg) => {
|
|
@@ -628,7 +842,7 @@ var AvantGateControlLayer = class {
|
|
|
628
842
|
});
|
|
629
843
|
const promptLength = processedMessages.reduce((sum, msg) => sum + msg.content.length, 0);
|
|
630
844
|
const estimatedPromptTokens = Math.ceil(promptLength / 4);
|
|
631
|
-
this.checkPreflightBudget(estimatedPromptTokens, modelToUse);
|
|
845
|
+
await this.checkPreflightBudget(estimatedPromptTokens, modelToUse);
|
|
632
846
|
let lastError;
|
|
633
847
|
let accumulatedPromptTokens = 0;
|
|
634
848
|
let accumulatedCompletionTokens = 0;
|
|
@@ -716,11 +930,11 @@ var AvantGateControlLayer = class {
|
|
|
716
930
|
}
|
|
717
931
|
}
|
|
718
932
|
throw lastError;
|
|
719
|
-
}
|
|
933
|
+
};
|
|
720
934
|
};
|
|
721
|
-
|
|
935
|
+
var createLLMControlLayer = (config) => {
|
|
722
936
|
return new AvantGateControlLayer(config);
|
|
723
|
-
}
|
|
937
|
+
};
|
|
724
938
|
var createAvantGate = createLLMControlLayer;
|
|
725
939
|
|
|
726
940
|
// src/prompts/prompt-template.ts
|
|
@@ -1051,6 +1265,7 @@ export {
|
|
|
1051
1265
|
PromptRegistry,
|
|
1052
1266
|
PromptTemplate,
|
|
1053
1267
|
SEED_MODEL_PRICES,
|
|
1268
|
+
SqlitePricingAdapter,
|
|
1054
1269
|
WorkflowAbortSignal,
|
|
1055
1270
|
WorkflowSagaRollbackError,
|
|
1056
1271
|
WorkflowValidationError,
|