@elsium-ai/gateway 0.2.2 → 0.3.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/dist/batch.d.ts +24 -0
- package/dist/batch.d.ts.map +1 -0
- package/dist/cache.d.ts +26 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/gateway.d.ts +2 -0
- package/dist/gateway.d.ts.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +663 -134
- package/dist/output-guardrails.d.ts +23 -0
- package/dist/output-guardrails.d.ts.map +1 -0
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/router.d.ts +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/security.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/batch.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CompletionRequest, LLMResponse } from '@elsium-ai/core';
|
|
2
|
+
import type { Gateway } from './gateway';
|
|
3
|
+
export interface BatchConfig {
|
|
4
|
+
concurrency?: number;
|
|
5
|
+
retryPerItem?: number;
|
|
6
|
+
onProgress?: (completed: number, total: number) => void;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
}
|
|
9
|
+
export interface BatchResultItem {
|
|
10
|
+
index: number;
|
|
11
|
+
success: boolean;
|
|
12
|
+
response?: LLMResponse;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface BatchResult {
|
|
16
|
+
results: BatchResultItem[];
|
|
17
|
+
totalSucceeded: number;
|
|
18
|
+
totalFailed: number;
|
|
19
|
+
totalDurationMs: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function createBatch(gateway: Gateway, config?: BatchConfig): {
|
|
22
|
+
execute(requests: CompletionRequest[]): Promise<BatchResult>;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=batch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAErE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAIxC,MAAM,WAAW,WAAW;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACvD,MAAM,CAAC,EAAE,WAAW,CAAA;CACpB;AAED,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,WAAW,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,eAAe,EAAE,CAAA;IAC1B,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;CACvB;AAED,wBAAgB,WAAW,CAC1B,OAAO,EAAE,OAAO,EAChB,MAAM,CAAC,EAAE,WAAW,GAClB;IAAE,OAAO,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;CAAE,CAiGlE"}
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { LLMResponse, Middleware, MiddlewareContext } from '@elsium-ai/core';
|
|
2
|
+
export interface CacheAdapter {
|
|
3
|
+
get(key: string): Promise<LLMResponse | null>;
|
|
4
|
+
set(key: string, value: LLMResponse, ttlMs: number): Promise<void>;
|
|
5
|
+
delete(key: string): Promise<void>;
|
|
6
|
+
clear(): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export interface CacheStats {
|
|
9
|
+
hits: number;
|
|
10
|
+
misses: number;
|
|
11
|
+
size: number;
|
|
12
|
+
hitRate: number;
|
|
13
|
+
}
|
|
14
|
+
export interface CacheMiddlewareConfig {
|
|
15
|
+
adapter?: CacheAdapter;
|
|
16
|
+
ttlMs?: number;
|
|
17
|
+
maxSize?: number;
|
|
18
|
+
keyFn?: (ctx: MiddlewareContext) => string;
|
|
19
|
+
shouldCache?: (ctx: MiddlewareContext, response: LLMResponse) => boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function createInMemoryCache(maxSize?: number): CacheAdapter;
|
|
22
|
+
export declare function cacheMiddleware(config?: CacheMiddlewareConfig): Middleware & {
|
|
23
|
+
readonly adapter: CacheAdapter;
|
|
24
|
+
stats(): CacheStats;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAKjF,MAAM,WAAW,YAAY;IAC5B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;IAC7C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB;AAED,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,qBAAqB;IACrC,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,MAAM,CAAA;IAC1C,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,EAAE,QAAQ,EAAE,WAAW,KAAK,OAAO,CAAA;CACxE;AAOD,wBAAgB,mBAAmB,CAAC,OAAO,SAAO,GAAG,YAAY,CAuChE;AAmBD,wBAAgB,eAAe,CAC9B,MAAM,CAAC,EAAE,qBAAqB,GAC5B,UAAU,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAAC,KAAK,IAAI,UAAU,CAAA;CAAE,CA8CtE"}
|
package/dist/gateway.d.ts
CHANGED
package/dist/gateway.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,iBAAiB,EACjB,WAAW,EACX,UAAU,EAEV,cAAc,
|
|
1
|
+
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,iBAAiB,EACjB,WAAW,EACX,UAAU,EAEV,cAAc,EAEd,QAAQ,EACR,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAEN,KAAK,YAAY,EAIjB,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAI5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAM7C,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,EAAE,CAAA;IACzB,IAAI,CAAC,EAAE,OAAO,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACxC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,OAAO;IACvB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAC1D,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,YAAY,CAAA;IAChD,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,iBAAiB,GAAG;QAAE,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC;QAC3E,IAAI,EAAE,CAAC,CAAA;QACP,QAAQ,EAAE,WAAW,CAAA;KACrB,CAAC,CAAA;IACF,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAA;IAC9B,QAAQ,IAAI,QAAQ,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAA;CACvC;AAQD,wBAAgB,uBAAuB,CACtC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,WAAW,GAC9C,IAAI,CAEN;AAiID,wBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CA0ItD"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,13 @@ export { securityMiddleware, detectPromptInjection, detectJailbreak, redactSecre
|
|
|
11
11
|
export type { SecurityMiddlewareConfig, SecurityViolation, SecurityResult, DataClassification, ClassificationResult, } from './security';
|
|
12
12
|
export { createBulkhead, bulkheadMiddleware } from './bulkhead';
|
|
13
13
|
export type { BulkheadConfig, Bulkhead } from './bulkhead';
|
|
14
|
+
export { cacheMiddleware, createInMemoryCache } from './cache';
|
|
15
|
+
export type { CacheAdapter, CacheStats, CacheMiddlewareConfig } from './cache';
|
|
16
|
+
export { outputGuardrailMiddleware } from './output-guardrails';
|
|
17
|
+
export type { OutputGuardrailConfig, OutputGuardrailRule, OutputViolation, } from './output-guardrails';
|
|
14
18
|
export { calculateCost, registerPricing } from './pricing';
|
|
19
|
+
export { createBatch } from './batch';
|
|
20
|
+
export type { BatchConfig, BatchResult, BatchResultItem } from './batch';
|
|
15
21
|
export { createProviderMesh } from './router';
|
|
16
22
|
export type { ProviderMeshConfig, ProviderEntry, RoutingStrategy, ProviderMesh } from './router';
|
|
17
23
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAA;AAC5D,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAGvD,YAAY,EACX,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,SAAS,GACT,MAAM,YAAY,CAAA;AACnB,OAAO,EACN,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,EACb,wBAAwB,EACxB,mBAAmB,GACnB,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAGzD,OAAO,EACN,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,GACd,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAG7C,OAAO,EACN,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,aAAa,EACb,oBAAoB,EACpB,eAAe,GACf,MAAM,YAAY,CAAA;AACnB,YAAY,EACX,wBAAwB,EACxB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,GACpB,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/D,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAG1D,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAG1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAC7C,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAA;AAC5D,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAGvD,YAAY,EACX,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,SAAS,GACT,MAAM,YAAY,CAAA;AACnB,OAAO,EACN,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,EACb,wBAAwB,EACxB,mBAAmB,GACnB,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAGzD,OAAO,EACN,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,GACd,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAG7C,OAAO,EACN,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,aAAa,EACb,oBAAoB,EACpB,eAAe,GACf,MAAM,YAAY,CAAA;AACnB,YAAY,EACX,wBAAwB,EACxB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,GACpB,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/D,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAG1D,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC9D,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AAG9E,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,YAAY,EACX,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,GACf,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAG1D,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACrC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAGxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAC7C,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -394,6 +394,94 @@ function createLogger(options = {}) {
|
|
|
394
394
|
}
|
|
395
395
|
};
|
|
396
396
|
}
|
|
397
|
+
// ../core/src/schema.ts
|
|
398
|
+
var log = createLogger();
|
|
399
|
+
function zodDefKind(def) {
|
|
400
|
+
return typeof def.type === "string" ? def.type : def.typeName;
|
|
401
|
+
}
|
|
402
|
+
function zodObjectToJsonSchema(schema, convert) {
|
|
403
|
+
const shape = typeof schema.shape === "function" ? schema.shape() : schema.shape;
|
|
404
|
+
const properties = {};
|
|
405
|
+
const required = [];
|
|
406
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
407
|
+
const fieldSchema = value;
|
|
408
|
+
properties[key] = convert(fieldSchema);
|
|
409
|
+
const fieldDef = fieldSchema._def;
|
|
410
|
+
const fieldKind = zodDefKind(fieldDef);
|
|
411
|
+
if (fieldKind !== "optional" && fieldKind !== "ZodOptional" && fieldKind !== "default" && fieldKind !== "ZodDefault") {
|
|
412
|
+
required.push(key);
|
|
413
|
+
}
|
|
414
|
+
if (fieldDef.description) {
|
|
415
|
+
properties[key].description = fieldDef.description;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return { type: "object", properties, required };
|
|
419
|
+
}
|
|
420
|
+
function zodToJsonSchema(schema) {
|
|
421
|
+
if (!("_def" in schema))
|
|
422
|
+
return { type: "object" };
|
|
423
|
+
const def = schema._def;
|
|
424
|
+
const kind = zodDefKind(def);
|
|
425
|
+
switch (kind) {
|
|
426
|
+
case "object":
|
|
427
|
+
case "ZodObject":
|
|
428
|
+
return zodObjectToJsonSchema(def, zodToJsonSchema);
|
|
429
|
+
case "string":
|
|
430
|
+
case "ZodString":
|
|
431
|
+
return { type: "string" };
|
|
432
|
+
case "number":
|
|
433
|
+
case "ZodNumber":
|
|
434
|
+
return { type: "number" };
|
|
435
|
+
case "boolean":
|
|
436
|
+
case "ZodBoolean":
|
|
437
|
+
return { type: "boolean" };
|
|
438
|
+
case "array":
|
|
439
|
+
case "ZodArray":
|
|
440
|
+
return {
|
|
441
|
+
type: "array",
|
|
442
|
+
items: zodToJsonSchema(def.element ?? def.type)
|
|
443
|
+
};
|
|
444
|
+
case "enum":
|
|
445
|
+
case "ZodEnum": {
|
|
446
|
+
const values = def.values ?? (def.entries ? Object.values(def.entries) : []);
|
|
447
|
+
return { type: "string", enum: values };
|
|
448
|
+
}
|
|
449
|
+
case "optional":
|
|
450
|
+
case "ZodOptional":
|
|
451
|
+
return zodToJsonSchema(def.innerType);
|
|
452
|
+
case "default":
|
|
453
|
+
case "ZodDefault":
|
|
454
|
+
return zodToJsonSchema(def.innerType);
|
|
455
|
+
case "nullable":
|
|
456
|
+
case "ZodNullable": {
|
|
457
|
+
const inner = zodToJsonSchema(def.innerType);
|
|
458
|
+
return { ...inner, nullable: true };
|
|
459
|
+
}
|
|
460
|
+
case "ZodLiteral":
|
|
461
|
+
return { type: typeof def.value, const: def.value };
|
|
462
|
+
case "ZodUnion": {
|
|
463
|
+
const options = def.options.map(zodToJsonSchema);
|
|
464
|
+
return { anyOf: options };
|
|
465
|
+
}
|
|
466
|
+
case "ZodRecord":
|
|
467
|
+
return {
|
|
468
|
+
type: "object",
|
|
469
|
+
additionalProperties: def.valueType ? zodToJsonSchema(def.valueType) : { type: "string" }
|
|
470
|
+
};
|
|
471
|
+
case "ZodTuple": {
|
|
472
|
+
const items = (def.items ?? []).map(zodToJsonSchema);
|
|
473
|
+
return { type: "array", prefixItems: items, minItems: items.length, maxItems: items.length };
|
|
474
|
+
}
|
|
475
|
+
case "ZodDate":
|
|
476
|
+
return { type: "string", format: "date-time" };
|
|
477
|
+
default:
|
|
478
|
+
log.warn(`zodToJsonSchema: unsupported type ${kind}, defaulting to string`);
|
|
479
|
+
return { type: "string" };
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
// ../core/src/registry.ts
|
|
483
|
+
var log2 = createLogger();
|
|
484
|
+
var BLOCKED_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
397
485
|
// ../core/src/circuit-breaker.ts
|
|
398
486
|
function defaultShouldCount(error) {
|
|
399
487
|
if (error && typeof error === "object" && "retryable" in error) {
|
|
@@ -560,16 +648,16 @@ function composeMiddleware(middlewares) {
|
|
|
560
648
|
};
|
|
561
649
|
}
|
|
562
650
|
function loggingMiddleware(logger) {
|
|
563
|
-
const
|
|
651
|
+
const log3 = logger ?? createLogger({ level: "info" });
|
|
564
652
|
return async (ctx, next) => {
|
|
565
|
-
|
|
653
|
+
log3.info("LLM request", {
|
|
566
654
|
provider: ctx.provider,
|
|
567
655
|
model: ctx.model,
|
|
568
656
|
traceId: ctx.traceId,
|
|
569
657
|
messageCount: ctx.request.messages.length
|
|
570
658
|
});
|
|
571
659
|
const response = await next(ctx);
|
|
572
|
-
|
|
660
|
+
log3.info("LLM response", {
|
|
573
661
|
provider: ctx.provider,
|
|
574
662
|
model: ctx.model,
|
|
575
663
|
traceId: ctx.traceId,
|
|
@@ -705,7 +793,7 @@ function xrayMiddleware(options = {}) {
|
|
|
705
793
|
}
|
|
706
794
|
|
|
707
795
|
// src/pricing.ts
|
|
708
|
-
var
|
|
796
|
+
var log3 = createLogger();
|
|
709
797
|
var PRICING = {
|
|
710
798
|
"claude-opus-4-6": { inputPerMillion: 15, outputPerMillion: 75 },
|
|
711
799
|
"claude-sonnet-4-6": { inputPerMillion: 3, outputPerMillion: 15 },
|
|
@@ -743,7 +831,7 @@ function resolveModelName(model) {
|
|
|
743
831
|
function calculateCost(model, usage) {
|
|
744
832
|
const pricing = PRICING[resolveModelName(model)];
|
|
745
833
|
if (!pricing) {
|
|
746
|
-
|
|
834
|
+
log3.warn(`Unknown model "${model}" — cost will be reported as $0. Register pricing with registerPricing().`);
|
|
747
835
|
return {
|
|
748
836
|
inputCost: 0,
|
|
749
837
|
outputCost: 0,
|
|
@@ -837,15 +925,33 @@ function createAnthropicProvider(config) {
|
|
|
837
925
|
if (part.type === "text")
|
|
838
926
|
return { type: "text", text: part.text };
|
|
839
927
|
if (part.type === "image" && part.source?.type === "base64") {
|
|
928
|
+
const src = part.source;
|
|
840
929
|
return {
|
|
841
930
|
type: "image",
|
|
842
931
|
source: {
|
|
843
932
|
type: "base64",
|
|
844
|
-
media_type:
|
|
845
|
-
data:
|
|
933
|
+
media_type: src.mediaType,
|
|
934
|
+
data: src.data
|
|
846
935
|
}
|
|
847
936
|
};
|
|
848
937
|
}
|
|
938
|
+
if (part.type === "document" && part.source) {
|
|
939
|
+
if (part.source.type === "base64") {
|
|
940
|
+
const src = part.source;
|
|
941
|
+
return {
|
|
942
|
+
type: "document",
|
|
943
|
+
source: {
|
|
944
|
+
type: "base64",
|
|
945
|
+
media_type: src.mediaType,
|
|
946
|
+
data: src.data
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
return { type: "text", text: "[document: url source not supported by Anthropic]" };
|
|
951
|
+
}
|
|
952
|
+
if (part.type === "audio") {
|
|
953
|
+
return { type: "text", text: "[audio content not supported by this provider]" };
|
|
954
|
+
}
|
|
849
955
|
return { type: "text", text: "[unsupported content]" };
|
|
850
956
|
}
|
|
851
957
|
function formatMultipartContent(msg, role) {
|
|
@@ -954,6 +1060,16 @@ function createAnthropicProvider(config) {
|
|
|
954
1060
|
const tools = formatTools(req.tools);
|
|
955
1061
|
if (tools)
|
|
956
1062
|
body.tools = tools;
|
|
1063
|
+
if (req.schema) {
|
|
1064
|
+
const jsonSchema = zodToJsonSchema(req.schema);
|
|
1065
|
+
const structuredTool = {
|
|
1066
|
+
name: "_structured_output",
|
|
1067
|
+
description: "Return structured output matching the required schema",
|
|
1068
|
+
input_schema: jsonSchema
|
|
1069
|
+
};
|
|
1070
|
+
body.tools = [...tools ?? [], structuredTool];
|
|
1071
|
+
body.tool_choice = { type: "tool", name: "_structured_output" };
|
|
1072
|
+
}
|
|
957
1073
|
const startTime = performance.now();
|
|
958
1074
|
const raw = await retry(async () => {
|
|
959
1075
|
const controller = new AbortController;
|
|
@@ -1163,7 +1279,31 @@ function createGoogleProvider(config) {
|
|
|
1163
1279
|
return { role, parts };
|
|
1164
1280
|
}
|
|
1165
1281
|
function formatGeminiMultipartContent(msg, role) {
|
|
1166
|
-
const parts =
|
|
1282
|
+
const parts = [];
|
|
1283
|
+
for (const p of msg.content) {
|
|
1284
|
+
if (p.type === "text") {
|
|
1285
|
+
parts.push({ text: p.text });
|
|
1286
|
+
} else if (p.type === "image") {
|
|
1287
|
+
const img = p;
|
|
1288
|
+
if (img.source.type === "base64") {
|
|
1289
|
+
parts.push({ inlineData: { mimeType: img.source.mediaType, data: img.source.data } });
|
|
1290
|
+
} else {
|
|
1291
|
+
parts.push({ fileData: { mimeType: "image/jpeg", fileUri: img.source.url } });
|
|
1292
|
+
}
|
|
1293
|
+
} else if (p.type === "audio" || p.type === "document") {
|
|
1294
|
+
const media = p;
|
|
1295
|
+
if (media.source.type === "base64") {
|
|
1296
|
+
parts.push({
|
|
1297
|
+
inlineData: { mimeType: media.source.mediaType, data: media.source.data }
|
|
1298
|
+
});
|
|
1299
|
+
} else {
|
|
1300
|
+
const urlSource = media.source;
|
|
1301
|
+
parts.push({
|
|
1302
|
+
fileData: { mimeType: "application/octet-stream", fileUri: urlSource.url }
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1167
1307
|
return { role, parts };
|
|
1168
1308
|
}
|
|
1169
1309
|
function formatMessages(messages) {
|
|
@@ -1260,6 +1400,10 @@ function createGoogleProvider(config) {
|
|
|
1260
1400
|
config2.topP = req.topP;
|
|
1261
1401
|
if (req.stopSequences?.length)
|
|
1262
1402
|
config2.stopSequences = req.stopSequences;
|
|
1403
|
+
if (req.schema) {
|
|
1404
|
+
config2.responseMimeType = "application/json";
|
|
1405
|
+
config2.responseSchema = zodToJsonSchema(req.schema);
|
|
1406
|
+
}
|
|
1263
1407
|
return config2;
|
|
1264
1408
|
}
|
|
1265
1409
|
function buildRequestBody(req) {
|
|
@@ -1338,7 +1482,8 @@ async function handleGoogleErrorResponse(response) {
|
|
|
1338
1482
|
throw ElsiumError.authError("google");
|
|
1339
1483
|
}
|
|
1340
1484
|
if (response.status === 429) {
|
|
1341
|
-
|
|
1485
|
+
const retryAfter = response.headers.get("retry-after");
|
|
1486
|
+
throw ElsiumError.rateLimit("google", retryAfter ? Number.parseInt(retryAfter) * 1000 : undefined);
|
|
1342
1487
|
}
|
|
1343
1488
|
throw ElsiumError.providerError(`Google API error ${response.status}: ${errorBody}`, {
|
|
1344
1489
|
provider: "google",
|
|
@@ -1524,6 +1669,43 @@ function createOpenAIProvider(config) {
|
|
|
1524
1669
|
}
|
|
1525
1670
|
return openaiMsg;
|
|
1526
1671
|
}
|
|
1672
|
+
function formatUserContent(msg) {
|
|
1673
|
+
if (typeof msg.content === "string")
|
|
1674
|
+
return msg.content;
|
|
1675
|
+
const parts = [];
|
|
1676
|
+
for (const part of msg.content) {
|
|
1677
|
+
if (part.type === "text") {
|
|
1678
|
+
parts.push({ type: "text", text: part.text });
|
|
1679
|
+
} else if (part.type === "image") {
|
|
1680
|
+
if (part.source.type === "base64") {
|
|
1681
|
+
const url = `data:${part.source.mediaType};base64,${part.source.data}`;
|
|
1682
|
+
parts.push({ type: "image_url", image_url: { url } });
|
|
1683
|
+
} else {
|
|
1684
|
+
parts.push({ type: "image_url", image_url: { url: part.source.url } });
|
|
1685
|
+
}
|
|
1686
|
+
} else if (part.type === "audio") {
|
|
1687
|
+
if (part.source.type === "base64") {
|
|
1688
|
+
const format = part.source.mediaType.split("/")[1] ?? "wav";
|
|
1689
|
+
parts.push({
|
|
1690
|
+
type: "input_audio",
|
|
1691
|
+
input_audio: { data: part.source.data, format }
|
|
1692
|
+
});
|
|
1693
|
+
} else {
|
|
1694
|
+
parts.push({ type: "text", text: "[audio: url source requires file upload]" });
|
|
1695
|
+
}
|
|
1696
|
+
} else if (part.type === "document") {
|
|
1697
|
+
if (part.source.type === "base64") {
|
|
1698
|
+
parts.push({
|
|
1699
|
+
type: "text",
|
|
1700
|
+
text: `[document: ${part.source.mediaType} content attached as base64]`
|
|
1701
|
+
});
|
|
1702
|
+
} else {
|
|
1703
|
+
parts.push({ type: "text", text: `[document: ${part.source.url}]` });
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
return parts;
|
|
1708
|
+
}
|
|
1527
1709
|
function formatMessages(messages) {
|
|
1528
1710
|
const formatted = [];
|
|
1529
1711
|
for (const msg of messages) {
|
|
@@ -1539,7 +1721,7 @@ function createOpenAIProvider(config) {
|
|
|
1539
1721
|
formatted.push(formatAssistantMessage(msg));
|
|
1540
1722
|
continue;
|
|
1541
1723
|
}
|
|
1542
|
-
formatted.push({ role: "user", content:
|
|
1724
|
+
formatted.push({ role: "user", content: formatUserContent(msg) });
|
|
1543
1725
|
}
|
|
1544
1726
|
return formatted;
|
|
1545
1727
|
}
|
|
@@ -1616,6 +1798,17 @@ function createOpenAIProvider(config) {
|
|
|
1616
1798
|
const tools = formatTools(req.tools);
|
|
1617
1799
|
if (tools)
|
|
1618
1800
|
body.tools = tools;
|
|
1801
|
+
if (req.schema) {
|
|
1802
|
+
const jsonSchema = zodToJsonSchema(req.schema);
|
|
1803
|
+
body.response_format = {
|
|
1804
|
+
type: "json_schema",
|
|
1805
|
+
json_schema: {
|
|
1806
|
+
name: "structured_output",
|
|
1807
|
+
strict: true,
|
|
1808
|
+
schema: jsonSchema
|
|
1809
|
+
}
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1619
1812
|
const startTime = performance.now();
|
|
1620
1813
|
const raw = await retry(async () => {
|
|
1621
1814
|
const controller = new AbortController;
|
|
@@ -1778,7 +1971,7 @@ var PROVIDER_FACTORIES = {
|
|
|
1778
1971
|
function registerProviderFactory(name, factory) {
|
|
1779
1972
|
PROVIDER_FACTORIES[name] = factory;
|
|
1780
1973
|
}
|
|
1781
|
-
function
|
|
1974
|
+
function validateGatewayConfig(config) {
|
|
1782
1975
|
const factory = PROVIDER_FACTORIES[config.provider];
|
|
1783
1976
|
if (!factory) {
|
|
1784
1977
|
throw new ElsiumError({
|
|
@@ -1787,21 +1980,92 @@ function gateway(config) {
|
|
|
1787
1980
|
retryable: false
|
|
1788
1981
|
});
|
|
1789
1982
|
}
|
|
1983
|
+
if (typeof config.apiKey !== "string" || config.apiKey.trim() === "") {
|
|
1984
|
+
throw new ElsiumError({
|
|
1985
|
+
code: "CONFIG_ERROR",
|
|
1986
|
+
message: "apiKey must be a non-empty string",
|
|
1987
|
+
retryable: false
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
if (config.timeout !== undefined && (!Number.isFinite(config.timeout) || config.timeout <= 0)) {
|
|
1991
|
+
throw new ElsiumError({
|
|
1992
|
+
code: "CONFIG_ERROR",
|
|
1993
|
+
message: "timeout must be a positive finite number",
|
|
1994
|
+
retryable: false
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
if (config.maxRetries !== undefined && (!Number.isFinite(config.maxRetries) || !Number.isInteger(config.maxRetries) || config.maxRetries < 0)) {
|
|
1998
|
+
throw new ElsiumError({
|
|
1999
|
+
code: "CONFIG_ERROR",
|
|
2000
|
+
message: "maxRetries must be a non-negative finite integer",
|
|
2001
|
+
retryable: false
|
|
2002
|
+
});
|
|
2003
|
+
}
|
|
2004
|
+
return factory;
|
|
2005
|
+
}
|
|
2006
|
+
function autoRegisterProvider(provider) {
|
|
2007
|
+
if (!provider.metadata)
|
|
2008
|
+
return;
|
|
2009
|
+
registerProviderMetadata(provider.name, provider.metadata);
|
|
2010
|
+
if (!provider.metadata.pricing)
|
|
2011
|
+
return;
|
|
2012
|
+
for (const [model, pricing] of Object.entries(provider.metadata.pricing)) {
|
|
2013
|
+
registerPricing(model, pricing);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
function validateRequestLimits(request, maxMessages, maxInputTokens) {
|
|
2017
|
+
if (request.messages.length > maxMessages) {
|
|
2018
|
+
throw ElsiumError.validation(`Message count ${request.messages.length} exceeds limit of ${maxMessages}`);
|
|
2019
|
+
}
|
|
2020
|
+
let estimatedTokens = 0;
|
|
2021
|
+
for (const msg of request.messages) {
|
|
2022
|
+
const text = typeof msg.content === "string" ? msg.content : msg.content.map((p) => p.type === "text" ? p.text : "").join("");
|
|
2023
|
+
estimatedTokens += Math.ceil(text.length / 4);
|
|
2024
|
+
}
|
|
2025
|
+
if (estimatedTokens > maxInputTokens) {
|
|
2026
|
+
throw ElsiumError.validation(`Estimated input tokens (~${estimatedTokens}) exceeds limit of ${maxInputTokens}`);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
function buildMiddlewareContext(req, providerName, defaultModel, metadata) {
|
|
2030
|
+
return {
|
|
2031
|
+
request: req,
|
|
2032
|
+
provider: providerName,
|
|
2033
|
+
model: req.model ?? defaultModel,
|
|
2034
|
+
traceId: generateTraceId(),
|
|
2035
|
+
startTime: performance.now(),
|
|
2036
|
+
metadata
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
async function accumulateStreamEvents(stream, emit) {
|
|
2040
|
+
let textContent = "";
|
|
2041
|
+
let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
2042
|
+
let stopReason = "end_turn";
|
|
2043
|
+
let id = "";
|
|
2044
|
+
for await (const event of stream) {
|
|
2045
|
+
emit(event);
|
|
2046
|
+
if (event.type === "text_delta") {
|
|
2047
|
+
textContent += event.text;
|
|
2048
|
+
} else if (event.type === "message_end") {
|
|
2049
|
+
usage = event.usage;
|
|
2050
|
+
stopReason = event.stopReason;
|
|
2051
|
+
} else if (event.type === "message_start") {
|
|
2052
|
+
id = event.id;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
return { textContent, usage, stopReason, id };
|
|
2056
|
+
}
|
|
2057
|
+
function gateway(config) {
|
|
2058
|
+
const factory = validateGatewayConfig(config);
|
|
1790
2059
|
const provider = factory({
|
|
1791
2060
|
apiKey: config.apiKey,
|
|
1792
2061
|
baseUrl: config.baseUrl,
|
|
1793
2062
|
timeout: config.timeout,
|
|
1794
2063
|
maxRetries: config.maxRetries
|
|
1795
2064
|
});
|
|
1796
|
-
|
|
1797
|
-
registerProviderMetadata(provider.name, provider.metadata);
|
|
1798
|
-
if (provider.metadata.pricing) {
|
|
1799
|
-
for (const [model, pricing] of Object.entries(provider.metadata.pricing)) {
|
|
1800
|
-
registerPricing(model, pricing);
|
|
1801
|
-
}
|
|
1802
|
-
}
|
|
1803
|
-
}
|
|
2065
|
+
autoRegisterProvider(provider);
|
|
1804
2066
|
const defaultModel = config.model ?? provider.defaultModel;
|
|
2067
|
+
const maxMessages = config.maxMessages ?? 1000;
|
|
2068
|
+
const maxInputTokens = config.maxInputTokens ?? 1e6;
|
|
1805
2069
|
let xrayStore = null;
|
|
1806
2070
|
const allMiddleware = [...config.middleware ?? []];
|
|
1807
2071
|
if (config.xray) {
|
|
@@ -1816,14 +2080,7 @@ function gateway(config) {
|
|
|
1816
2080
|
if (!composedMiddleware) {
|
|
1817
2081
|
return provider.complete(req);
|
|
1818
2082
|
}
|
|
1819
|
-
const ctx = {
|
|
1820
|
-
request: req,
|
|
1821
|
-
provider: provider.name,
|
|
1822
|
-
model: req.model ?? defaultModel,
|
|
1823
|
-
traceId: generateTraceId(),
|
|
1824
|
-
startTime: performance.now(),
|
|
1825
|
-
metadata: request.metadata ?? {}
|
|
1826
|
-
};
|
|
2083
|
+
const ctx = buildMiddlewareContext(req, provider.name, defaultModel, request.metadata ?? {});
|
|
1827
2084
|
return composedMiddleware(ctx, async (c) => provider.complete(c.request));
|
|
1828
2085
|
}
|
|
1829
2086
|
return {
|
|
@@ -1835,34 +2092,27 @@ function gateway(config) {
|
|
|
1835
2092
|
return xrayStore?.callHistory(limit) ?? [];
|
|
1836
2093
|
},
|
|
1837
2094
|
async complete(request) {
|
|
2095
|
+
validateRequestLimits(request, maxMessages, maxInputTokens);
|
|
1838
2096
|
return executeWithMiddleware(request);
|
|
1839
2097
|
},
|
|
1840
2098
|
stream(request) {
|
|
2099
|
+
validateRequestLimits(request, maxMessages, maxInputTokens);
|
|
1841
2100
|
const req = { ...request, model: request.model ?? defaultModel };
|
|
1842
2101
|
if (composedMiddleware) {
|
|
1843
|
-
const ctx = {
|
|
1844
|
-
request: req,
|
|
1845
|
-
provider: provider.name,
|
|
1846
|
-
model: req.model ?? defaultModel,
|
|
1847
|
-
traceId: generateTraceId(),
|
|
1848
|
-
startTime: performance.now(),
|
|
1849
|
-
metadata: request.metadata ?? {}
|
|
1850
|
-
};
|
|
2102
|
+
const ctx = buildMiddlewareContext(req, provider.name, defaultModel, request.metadata ?? {});
|
|
1851
2103
|
return createStream(async (emit) => {
|
|
1852
2104
|
await composedMiddleware(ctx, async (c) => {
|
|
1853
|
-
const
|
|
1854
|
-
|
|
1855
|
-
emit(event);
|
|
1856
|
-
}
|
|
2105
|
+
const result = await accumulateStreamEvents(provider.stream(c.request), emit);
|
|
2106
|
+
const latencyMs = Math.round(performance.now() - ctx.startTime);
|
|
1857
2107
|
return {
|
|
1858
|
-
id:
|
|
1859
|
-
message: { role: "assistant", content:
|
|
1860
|
-
usage:
|
|
1861
|
-
cost:
|
|
2108
|
+
id: result.id,
|
|
2109
|
+
message: { role: "assistant", content: result.textContent },
|
|
2110
|
+
usage: result.usage,
|
|
2111
|
+
cost: calculateCost(c.model, result.usage),
|
|
1862
2112
|
model: c.model,
|
|
1863
2113
|
provider: provider.name,
|
|
1864
|
-
stopReason:
|
|
1865
|
-
latencyMs
|
|
2114
|
+
stopReason: result.stopReason,
|
|
2115
|
+
latencyMs,
|
|
1866
2116
|
traceId: ctx.traceId
|
|
1867
2117
|
};
|
|
1868
2118
|
});
|
|
@@ -1872,107 +2122,46 @@ function gateway(config) {
|
|
|
1872
2122
|
},
|
|
1873
2123
|
async generate(request) {
|
|
1874
2124
|
const { schema, ...rest } = request;
|
|
1875
|
-
const jsonSchema =
|
|
1876
|
-
const systemPrompt = [
|
|
1877
|
-
rest.system ?? "",
|
|
1878
|
-
"You MUST respond with valid JSON matching this schema:",
|
|
1879
|
-
JSON.stringify(jsonSchema, null, 2),
|
|
1880
|
-
"Respond ONLY with the JSON object, no markdown or explanation."
|
|
1881
|
-
].filter(Boolean).join(`
|
|
1882
|
-
|
|
1883
|
-
`);
|
|
2125
|
+
const jsonSchema = zodToJsonSchema(schema);
|
|
1884
2126
|
const response = await executeWithMiddleware({
|
|
1885
2127
|
...rest,
|
|
1886
|
-
|
|
2128
|
+
schema,
|
|
2129
|
+
system: [
|
|
2130
|
+
rest.system ?? "",
|
|
2131
|
+
"You MUST respond with valid JSON matching this schema:",
|
|
2132
|
+
JSON.stringify(jsonSchema, null, 2),
|
|
2133
|
+
"Respond ONLY with the JSON object, no markdown or explanation."
|
|
2134
|
+
].filter(Boolean).join(`
|
|
2135
|
+
|
|
2136
|
+
`)
|
|
1887
2137
|
});
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
}
|
|
2138
|
+
let parsed;
|
|
2139
|
+
if (response.stopReason === "tool_use" && response.message.toolCalls?.length) {
|
|
2140
|
+
const structuredCall = response.message.toolCalls.find((tc) => tc.name === "_structured_output");
|
|
2141
|
+
if (structuredCall) {
|
|
2142
|
+
parsed = structuredCall.arguments;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
if (parsed === undefined) {
|
|
2146
|
+
const text = typeof response.message.content === "string" ? response.message.content : "";
|
|
2147
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
2148
|
+
if (!jsonMatch) {
|
|
2149
|
+
throw ElsiumError.validation("LLM response did not contain valid JSON", {
|
|
2150
|
+
response: text
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
parsed = JSON.parse(jsonMatch[0]);
|
|
1894
2154
|
}
|
|
1895
|
-
const parsed = JSON.parse(jsonMatch[0]);
|
|
1896
2155
|
const result = schema.safeParse(parsed);
|
|
1897
2156
|
if (!result.success) {
|
|
1898
2157
|
throw ElsiumError.validation("LLM response did not match schema", {
|
|
1899
|
-
errors: result.error.issues
|
|
1900
|
-
response: text
|
|
2158
|
+
errors: result.error.issues
|
|
1901
2159
|
});
|
|
1902
2160
|
}
|
|
1903
2161
|
return { data: result.data, response };
|
|
1904
2162
|
}
|
|
1905
2163
|
};
|
|
1906
2164
|
}
|
|
1907
|
-
function schemaToJsonSchema(schema) {
|
|
1908
|
-
try {
|
|
1909
|
-
if ("_def" in schema) {
|
|
1910
|
-
const def = schema._def;
|
|
1911
|
-
const result = convertZodDef(def);
|
|
1912
|
-
if (result)
|
|
1913
|
-
return result;
|
|
1914
|
-
}
|
|
1915
|
-
} catch {}
|
|
1916
|
-
return { type: "string" };
|
|
1917
|
-
}
|
|
1918
|
-
function zodDefKind(def) {
|
|
1919
|
-
return typeof def.type === "string" ? def.type : def.typeName;
|
|
1920
|
-
}
|
|
1921
|
-
function convertZodDef(def) {
|
|
1922
|
-
const kind = zodDefKind(def);
|
|
1923
|
-
switch (kind) {
|
|
1924
|
-
case "object":
|
|
1925
|
-
case "ZodObject":
|
|
1926
|
-
return convertZodObject(def);
|
|
1927
|
-
case "string":
|
|
1928
|
-
case "ZodString":
|
|
1929
|
-
return { type: "string" };
|
|
1930
|
-
case "number":
|
|
1931
|
-
case "ZodNumber":
|
|
1932
|
-
return { type: "number" };
|
|
1933
|
-
case "boolean":
|
|
1934
|
-
case "ZodBoolean":
|
|
1935
|
-
return { type: "boolean" };
|
|
1936
|
-
case "array":
|
|
1937
|
-
case "ZodArray":
|
|
1938
|
-
return convertZodArray(def);
|
|
1939
|
-
case "enum":
|
|
1940
|
-
case "ZodEnum": {
|
|
1941
|
-
const values = def.values ?? (def.entries ? Object.values(def.entries) : []);
|
|
1942
|
-
return { type: "string", enum: values };
|
|
1943
|
-
}
|
|
1944
|
-
case "optional":
|
|
1945
|
-
case "ZodOptional":
|
|
1946
|
-
return convertZodOptional(def);
|
|
1947
|
-
default:
|
|
1948
|
-
return null;
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
function convertZodObject(def) {
|
|
1952
|
-
if (!def.shape)
|
|
1953
|
-
return null;
|
|
1954
|
-
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
1955
|
-
const properties = {};
|
|
1956
|
-
const required = [];
|
|
1957
|
-
for (const [key, value] of Object.entries(shape)) {
|
|
1958
|
-
properties[key] = schemaToJsonSchema(value);
|
|
1959
|
-
const valDef = value._def;
|
|
1960
|
-
const valKind = zodDefKind(valDef);
|
|
1961
|
-
if (valKind !== "optional" && valKind !== "ZodOptional") {
|
|
1962
|
-
required.push(key);
|
|
1963
|
-
}
|
|
1964
|
-
}
|
|
1965
|
-
return { type: "object", properties, required };
|
|
1966
|
-
}
|
|
1967
|
-
function convertZodArray(def) {
|
|
1968
|
-
return {
|
|
1969
|
-
type: "array",
|
|
1970
|
-
items: schemaToJsonSchema(def.element ?? def.type)
|
|
1971
|
-
};
|
|
1972
|
-
}
|
|
1973
|
-
function convertZodOptional(def) {
|
|
1974
|
-
return schemaToJsonSchema(def.innerType ?? def.innerType);
|
|
1975
|
-
}
|
|
1976
2165
|
// src/security.ts
|
|
1977
2166
|
var INJECTION_PATTERNS = [
|
|
1978
2167
|
{
|
|
@@ -2223,6 +2412,12 @@ function redactResponseSecrets(response, config) {
|
|
|
2223
2412
|
}
|
|
2224
2413
|
function securityMiddleware(config) {
|
|
2225
2414
|
return async (ctx, next) => {
|
|
2415
|
+
if (ctx.request.system) {
|
|
2416
|
+
const sysViolations = scanMessageForViolations(ctx.request.system, config);
|
|
2417
|
+
if (sysViolations.length > 0) {
|
|
2418
|
+
reportAndThrow(sysViolations, config);
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2226
2421
|
for (const message of ctx.request.messages) {
|
|
2227
2422
|
const text = extractText(message.content);
|
|
2228
2423
|
if (!text)
|
|
@@ -2329,6 +2524,321 @@ function bulkheadMiddleware(config) {
|
|
|
2329
2524
|
return bulkhead.execute(() => next(ctx));
|
|
2330
2525
|
};
|
|
2331
2526
|
}
|
|
2527
|
+
// src/cache.ts
|
|
2528
|
+
import { createHash } from "node:crypto";
|
|
2529
|
+
var log4 = createLogger();
|
|
2530
|
+
function createInMemoryCache(maxSize = 1000) {
|
|
2531
|
+
const cache = new Map;
|
|
2532
|
+
function evict() {
|
|
2533
|
+
if (cache.size <= maxSize)
|
|
2534
|
+
return;
|
|
2535
|
+
const firstKey = cache.keys().next().value;
|
|
2536
|
+
if (firstKey !== undefined)
|
|
2537
|
+
cache.delete(firstKey);
|
|
2538
|
+
}
|
|
2539
|
+
return {
|
|
2540
|
+
async get(key) {
|
|
2541
|
+
const entry = cache.get(key);
|
|
2542
|
+
if (!entry)
|
|
2543
|
+
return null;
|
|
2544
|
+
if (Date.now() > entry.expiresAt) {
|
|
2545
|
+
cache.delete(key);
|
|
2546
|
+
return null;
|
|
2547
|
+
}
|
|
2548
|
+
cache.delete(key);
|
|
2549
|
+
cache.set(key, entry);
|
|
2550
|
+
return entry.value;
|
|
2551
|
+
},
|
|
2552
|
+
async set(key, value, ttlMs) {
|
|
2553
|
+
cache.set(key, { value, expiresAt: Date.now() + ttlMs });
|
|
2554
|
+
evict();
|
|
2555
|
+
},
|
|
2556
|
+
async delete(key) {
|
|
2557
|
+
cache.delete(key);
|
|
2558
|
+
},
|
|
2559
|
+
async clear() {
|
|
2560
|
+
cache.clear();
|
|
2561
|
+
}
|
|
2562
|
+
};
|
|
2563
|
+
}
|
|
2564
|
+
function defaultCacheKey(ctx) {
|
|
2565
|
+
const data = JSON.stringify({
|
|
2566
|
+
provider: ctx.provider,
|
|
2567
|
+
model: ctx.model,
|
|
2568
|
+
messages: ctx.request.messages,
|
|
2569
|
+
system: ctx.request.system,
|
|
2570
|
+
temperature: ctx.request.temperature
|
|
2571
|
+
});
|
|
2572
|
+
return createHash("sha256").update(data).digest("hex");
|
|
2573
|
+
}
|
|
2574
|
+
function defaultShouldCache(_ctx, response) {
|
|
2575
|
+
const temp = _ctx.request.temperature;
|
|
2576
|
+
if (temp !== undefined && temp !== 0)
|
|
2577
|
+
return false;
|
|
2578
|
+
return response.stopReason === "end_turn";
|
|
2579
|
+
}
|
|
2580
|
+
function cacheMiddleware(config) {
|
|
2581
|
+
const ttlMs = config?.ttlMs ?? 3600000;
|
|
2582
|
+
const adapter = config?.adapter ?? createInMemoryCache(config?.maxSize ?? 1000);
|
|
2583
|
+
const keyFn = config?.keyFn ?? defaultCacheKey;
|
|
2584
|
+
const shouldCache = config?.shouldCache ?? defaultShouldCache;
|
|
2585
|
+
let hits = 0;
|
|
2586
|
+
let misses = 0;
|
|
2587
|
+
const middleware = async (ctx, next) => {
|
|
2588
|
+
if (ctx.request.stream) {
|
|
2589
|
+
return next(ctx);
|
|
2590
|
+
}
|
|
2591
|
+
const key = keyFn(ctx);
|
|
2592
|
+
const cached = await adapter.get(key);
|
|
2593
|
+
if (cached) {
|
|
2594
|
+
hits++;
|
|
2595
|
+
log4.debug("Cache hit", { key: key.slice(0, 8), provider: ctx.provider });
|
|
2596
|
+
return cached;
|
|
2597
|
+
}
|
|
2598
|
+
misses++;
|
|
2599
|
+
const response = await next(ctx);
|
|
2600
|
+
if (shouldCache(ctx, response)) {
|
|
2601
|
+
await adapter.set(key, response, ttlMs);
|
|
2602
|
+
}
|
|
2603
|
+
return response;
|
|
2604
|
+
};
|
|
2605
|
+
return Object.assign(middleware, {
|
|
2606
|
+
adapter,
|
|
2607
|
+
stats() {
|
|
2608
|
+
const total = hits + misses;
|
|
2609
|
+
return {
|
|
2610
|
+
hits,
|
|
2611
|
+
misses,
|
|
2612
|
+
size: 0,
|
|
2613
|
+
hitRate: total > 0 ? hits / total : 0
|
|
2614
|
+
};
|
|
2615
|
+
}
|
|
2616
|
+
});
|
|
2617
|
+
}
|
|
2618
|
+
// src/output-guardrails.ts
|
|
2619
|
+
var log5 = createLogger();
|
|
2620
|
+
var PII_PATTERNS2 = [
|
|
2621
|
+
{
|
|
2622
|
+
pattern: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
|
|
2623
|
+
label: "email",
|
|
2624
|
+
replacement: "[REDACTED_EMAIL]"
|
|
2625
|
+
},
|
|
2626
|
+
{
|
|
2627
|
+
pattern: /(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
|
|
2628
|
+
label: "phone",
|
|
2629
|
+
replacement: "[REDACTED_PHONE]"
|
|
2630
|
+
},
|
|
2631
|
+
{
|
|
2632
|
+
pattern: /\b\d{3}-\d{2}-\d{4}\b/g,
|
|
2633
|
+
label: "ssn",
|
|
2634
|
+
replacement: "[REDACTED_SSN]"
|
|
2635
|
+
},
|
|
2636
|
+
{
|
|
2637
|
+
pattern: /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/g,
|
|
2638
|
+
label: "credit_card",
|
|
2639
|
+
replacement: "[REDACTED_CC]"
|
|
2640
|
+
}
|
|
2641
|
+
];
|
|
2642
|
+
var SECRET_PATTERNS2 = [
|
|
2643
|
+
{
|
|
2644
|
+
pattern: /\bsk-[A-Za-z0-9]{20,}\b/g,
|
|
2645
|
+
label: "api_key",
|
|
2646
|
+
replacement: "[REDACTED_API_KEY]"
|
|
2647
|
+
},
|
|
2648
|
+
{
|
|
2649
|
+
pattern: /\bpk-[A-Za-z0-9]{20,}\b/g,
|
|
2650
|
+
label: "api_key",
|
|
2651
|
+
replacement: "[REDACTED_API_KEY]"
|
|
2652
|
+
},
|
|
2653
|
+
{
|
|
2654
|
+
pattern: /\bAKIA[A-Z0-9]{16}\b/g,
|
|
2655
|
+
label: "aws_key",
|
|
2656
|
+
replacement: "[REDACTED_AWS_KEY]"
|
|
2657
|
+
}
|
|
2658
|
+
];
|
|
2659
|
+
function detectPII(text) {
|
|
2660
|
+
const violations = [];
|
|
2661
|
+
const normalized = text.normalize("NFKC");
|
|
2662
|
+
for (const { pattern, label } of [...PII_PATTERNS2, ...SECRET_PATTERNS2]) {
|
|
2663
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
2664
|
+
if (regex.test(normalized)) {
|
|
2665
|
+
violations.push({
|
|
2666
|
+
type: "pii",
|
|
2667
|
+
detail: `Detected ${label} in output`,
|
|
2668
|
+
pattern: label
|
|
2669
|
+
});
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
return violations;
|
|
2673
|
+
}
|
|
2674
|
+
function redactPII(text) {
|
|
2675
|
+
let result = text;
|
|
2676
|
+
for (const { pattern, replacement } of [...PII_PATTERNS2, ...SECRET_PATTERNS2]) {
|
|
2677
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
2678
|
+
result = result.replace(regex, replacement);
|
|
2679
|
+
}
|
|
2680
|
+
return result;
|
|
2681
|
+
}
|
|
2682
|
+
function checkContentPolicy(text, policy) {
|
|
2683
|
+
const violations = [];
|
|
2684
|
+
if (policy.maxResponseLength && text.length > policy.maxResponseLength) {
|
|
2685
|
+
violations.push({
|
|
2686
|
+
type: "content_policy",
|
|
2687
|
+
detail: `Response length ${text.length} exceeds max ${policy.maxResponseLength}`
|
|
2688
|
+
});
|
|
2689
|
+
}
|
|
2690
|
+
if (policy.blockedPatterns) {
|
|
2691
|
+
for (const pattern of policy.blockedPatterns) {
|
|
2692
|
+
if (pattern.test(text)) {
|
|
2693
|
+
violations.push({
|
|
2694
|
+
type: "content_policy",
|
|
2695
|
+
detail: `Response matches blocked pattern: ${pattern.source}`,
|
|
2696
|
+
pattern: pattern.source
|
|
2697
|
+
});
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
return violations;
|
|
2702
|
+
}
|
|
2703
|
+
function checkCustomRules(text, rules) {
|
|
2704
|
+
const violations = [];
|
|
2705
|
+
for (const rule of rules) {
|
|
2706
|
+
if (rule.pattern.test(text)) {
|
|
2707
|
+
violations.push({
|
|
2708
|
+
type: "custom_rule",
|
|
2709
|
+
detail: rule.message ?? `Output matched custom rule: ${rule.name}`,
|
|
2710
|
+
pattern: rule.pattern.source
|
|
2711
|
+
});
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
return violations;
|
|
2715
|
+
}
|
|
2716
|
+
function outputGuardrailMiddleware(config) {
|
|
2717
|
+
const mode = config.onViolation ?? "block";
|
|
2718
|
+
return async (ctx, next) => {
|
|
2719
|
+
const response = await next(ctx);
|
|
2720
|
+
const text = extractText(response.message.content);
|
|
2721
|
+
const violations = [];
|
|
2722
|
+
if (config.piiDetection) {
|
|
2723
|
+
violations.push(...detectPII(text));
|
|
2724
|
+
}
|
|
2725
|
+
if (config.contentPolicy) {
|
|
2726
|
+
violations.push(...checkContentPolicy(text, config.contentPolicy));
|
|
2727
|
+
}
|
|
2728
|
+
if (config.customRules?.length) {
|
|
2729
|
+
violations.push(...checkCustomRules(text, config.customRules));
|
|
2730
|
+
}
|
|
2731
|
+
if (violations.length === 0)
|
|
2732
|
+
return response;
|
|
2733
|
+
for (const v of violations) {
|
|
2734
|
+
config.onViolationCallback?.(v);
|
|
2735
|
+
}
|
|
2736
|
+
switch (mode) {
|
|
2737
|
+
case "block":
|
|
2738
|
+
throw ElsiumError.validation(`Output guardrail violation: ${violations.map((v) => v.detail).join("; ")}`, { violations });
|
|
2739
|
+
case "redact": {
|
|
2740
|
+
let redacted = text;
|
|
2741
|
+
if (config.piiDetection) {
|
|
2742
|
+
redacted = redactPII(redacted);
|
|
2743
|
+
}
|
|
2744
|
+
return {
|
|
2745
|
+
...response,
|
|
2746
|
+
message: { ...response.message, content: redacted }
|
|
2747
|
+
};
|
|
2748
|
+
}
|
|
2749
|
+
case "warn":
|
|
2750
|
+
log5.warn("Output guardrail violations detected", { violations });
|
|
2751
|
+
return response;
|
|
2752
|
+
}
|
|
2753
|
+
};
|
|
2754
|
+
}
|
|
2755
|
+
// src/batch.ts
|
|
2756
|
+
var log6 = createLogger();
|
|
2757
|
+
function createBatch(gateway2, config) {
|
|
2758
|
+
const concurrency = config?.concurrency ?? 5;
|
|
2759
|
+
const retryPerItem = config?.retryPerItem ?? 0;
|
|
2760
|
+
return {
|
|
2761
|
+
async execute(requests) {
|
|
2762
|
+
const startTime = performance.now();
|
|
2763
|
+
const results = new Array(requests.length);
|
|
2764
|
+
let completed = 0;
|
|
2765
|
+
let totalSucceeded = 0;
|
|
2766
|
+
let totalFailed = 0;
|
|
2767
|
+
let running = 0;
|
|
2768
|
+
let nextIndex = 0;
|
|
2769
|
+
const signal = config?.signal;
|
|
2770
|
+
async function processItem(index) {
|
|
2771
|
+
if (signal?.aborted) {
|
|
2772
|
+
results[index] = {
|
|
2773
|
+
index,
|
|
2774
|
+
success: false,
|
|
2775
|
+
error: "Batch cancelled"
|
|
2776
|
+
};
|
|
2777
|
+
totalFailed++;
|
|
2778
|
+
return;
|
|
2779
|
+
}
|
|
2780
|
+
let lastError;
|
|
2781
|
+
for (let attempt = 0;attempt <= retryPerItem; attempt++) {
|
|
2782
|
+
try {
|
|
2783
|
+
const response = await gateway2.complete(requests[index]);
|
|
2784
|
+
results[index] = { index, success: true, response };
|
|
2785
|
+
totalSucceeded++;
|
|
2786
|
+
return;
|
|
2787
|
+
} catch (err2) {
|
|
2788
|
+
lastError = err2 instanceof Error ? err2.message : String(err2);
|
|
2789
|
+
if (attempt < retryPerItem && err2 instanceof ElsiumError && err2.retryable) {
|
|
2790
|
+
continue;
|
|
2791
|
+
}
|
|
2792
|
+
break;
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
results[index] = { index, success: false, error: lastError };
|
|
2796
|
+
totalFailed++;
|
|
2797
|
+
}
|
|
2798
|
+
return new Promise((resolve) => {
|
|
2799
|
+
function scheduleNext() {
|
|
2800
|
+
while (running < concurrency && nextIndex < requests.length) {
|
|
2801
|
+
if (signal?.aborted) {
|
|
2802
|
+
for (let i = nextIndex;i < requests.length; i++) {
|
|
2803
|
+
results[i] = { index: i, success: false, error: "Batch cancelled" };
|
|
2804
|
+
totalFailed++;
|
|
2805
|
+
}
|
|
2806
|
+
nextIndex = requests.length;
|
|
2807
|
+
break;
|
|
2808
|
+
}
|
|
2809
|
+
const idx = nextIndex++;
|
|
2810
|
+
running++;
|
|
2811
|
+
processItem(idx).then(() => {
|
|
2812
|
+
running--;
|
|
2813
|
+
completed++;
|
|
2814
|
+
config?.onProgress?.(completed, requests.length);
|
|
2815
|
+
if (completed === requests.length) {
|
|
2816
|
+
resolve({
|
|
2817
|
+
results,
|
|
2818
|
+
totalSucceeded,
|
|
2819
|
+
totalFailed,
|
|
2820
|
+
totalDurationMs: Math.round(performance.now() - startTime)
|
|
2821
|
+
});
|
|
2822
|
+
} else {
|
|
2823
|
+
scheduleNext();
|
|
2824
|
+
}
|
|
2825
|
+
});
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
if (requests.length === 0) {
|
|
2829
|
+
resolve({
|
|
2830
|
+
results: [],
|
|
2831
|
+
totalSucceeded: 0,
|
|
2832
|
+
totalFailed: 0,
|
|
2833
|
+
totalDurationMs: 0
|
|
2834
|
+
});
|
|
2835
|
+
return;
|
|
2836
|
+
}
|
|
2837
|
+
scheduleNext();
|
|
2838
|
+
});
|
|
2839
|
+
}
|
|
2840
|
+
};
|
|
2841
|
+
}
|
|
2332
2842
|
// src/router.ts
|
|
2333
2843
|
var REASONING_KEYWORDS = /\b(prove|explain why|analyze|compare|contrast|evaluate|critique|debate|reason|deduce|infer|justify|argue|synthesize|hypothesize|derive)\b/i;
|
|
2334
2844
|
var CODE_KEYWORDS = /\b(implement|refactor|debug|optimize|architect|design pattern|algorithm|data structure|write code|code review|fix the bug|type system)\b/i;
|
|
@@ -2557,7 +3067,22 @@ function createProviderMesh(config) {
|
|
|
2557
3067
|
const available = sortedProviders.find((e) => isProviderAvailable(e.name));
|
|
2558
3068
|
const entry = available ?? sortedProviders[0];
|
|
2559
3069
|
const gw = getGateway(entry.name);
|
|
2560
|
-
|
|
3070
|
+
let resolvedStream = null;
|
|
3071
|
+
callWithCircuitBreaker(entry.name, () => {
|
|
3072
|
+
resolvedStream = gw.stream({ ...request, model: request.model ?? entry.model });
|
|
3073
|
+
return Promise.resolve(resolvedStream);
|
|
3074
|
+
}).catch(() => {});
|
|
3075
|
+
if (resolvedStream === null) {
|
|
3076
|
+
const err2 = new ElsiumError({
|
|
3077
|
+
code: "PROVIDER_ERROR",
|
|
3078
|
+
message: "Circuit breaker is open",
|
|
3079
|
+
retryable: true
|
|
3080
|
+
});
|
|
3081
|
+
return new ElsiumStream(async function* () {
|
|
3082
|
+
yield { type: "error", error: err2 };
|
|
3083
|
+
}());
|
|
3084
|
+
}
|
|
3085
|
+
return resolvedStream;
|
|
2561
3086
|
}
|
|
2562
3087
|
};
|
|
2563
3088
|
}
|
|
@@ -2569,6 +3094,7 @@ export {
|
|
|
2569
3094
|
registerProvider,
|
|
2570
3095
|
registerPricing,
|
|
2571
3096
|
redactSecrets,
|
|
3097
|
+
outputGuardrailMiddleware,
|
|
2572
3098
|
loggingMiddleware,
|
|
2573
3099
|
listProviders,
|
|
2574
3100
|
getProviderMetadata,
|
|
@@ -2578,13 +3104,16 @@ export {
|
|
|
2578
3104
|
detectJailbreak,
|
|
2579
3105
|
createProviderMesh,
|
|
2580
3106
|
createOpenAIProvider,
|
|
3107
|
+
createInMemoryCache,
|
|
2581
3108
|
createGoogleProvider,
|
|
2582
3109
|
createBulkhead,
|
|
3110
|
+
createBatch,
|
|
2583
3111
|
createAnthropicProvider,
|
|
2584
3112
|
costTrackingMiddleware,
|
|
2585
3113
|
composeMiddleware,
|
|
2586
3114
|
classifyContent,
|
|
2587
3115
|
checkBlockedPatterns,
|
|
2588
3116
|
calculateCost,
|
|
3117
|
+
cacheMiddleware,
|
|
2589
3118
|
bulkheadMiddleware
|
|
2590
3119
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Middleware } from '@elsium-ai/core';
|
|
2
|
+
export interface OutputViolation {
|
|
3
|
+
type: 'pii' | 'content_policy' | 'custom_rule';
|
|
4
|
+
detail: string;
|
|
5
|
+
pattern?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface OutputGuardrailRule {
|
|
8
|
+
name: string;
|
|
9
|
+
pattern: RegExp;
|
|
10
|
+
message?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface OutputGuardrailConfig {
|
|
13
|
+
piiDetection?: boolean;
|
|
14
|
+
contentPolicy?: {
|
|
15
|
+
blockedPatterns?: RegExp[];
|
|
16
|
+
maxResponseLength?: number;
|
|
17
|
+
};
|
|
18
|
+
customRules?: OutputGuardrailRule[];
|
|
19
|
+
onViolation?: 'block' | 'redact' | 'warn';
|
|
20
|
+
onViolationCallback?: (violation: OutputViolation) => void;
|
|
21
|
+
}
|
|
22
|
+
export declare function outputGuardrailMiddleware(config: OutputGuardrailConfig): Middleware;
|
|
23
|
+
//# sourceMappingURL=output-guardrails.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-guardrails.d.ts","sourceRoot":"","sources":["../src/output-guardrails.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAKjD,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,KAAK,GAAG,gBAAgB,GAAG,aAAa,CAAA;IAC9C,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,qBAAqB;IACrC,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,aAAa,CAAC,EAAE;QACf,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;QAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAC1B,CAAA;IACD,WAAW,CAAC,EAAE,mBAAmB,EAAE,CAAA;IACnC,WAAW,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IACzC,mBAAmB,CAAC,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAA;CAC1D;AA+GD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,qBAAqB,GAAG,UAAU,CAiDnF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,EAMN,KAAK,cAAc,
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,EAMN,KAAK,cAAc,EAUnB,MAAM,iBAAiB,CAAA;AAExB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AA2C9C,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,cAAc,GAAG,WAAW,CAoV3E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"AAAA,OAAO,EAMN,KAAK,cAAc,
|
|
1
|
+
{"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/providers/google.ts"],"names":[],"mappings":"AAAA,OAAO,EAMN,KAAK,cAAc,EAUnB,MAAM,iBAAiB,CAAA;AAExB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAqC9C,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,WAAW,CAiRxE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EAMN,KAAK,cAAc,
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EAMN,KAAK,cAAc,EAUnB,MAAM,iBAAiB,CAAA;AAExB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAqD9C,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,WAAW,CAqUxE"}
|
package/dist/router.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CompletionRequest, LLMResponse } from '@elsium-ai/core';
|
|
2
|
-
import { type CircuitBreakerConfig,
|
|
2
|
+
import { type CircuitBreakerConfig, ElsiumStream } from '@elsium-ai/core';
|
|
3
3
|
export type RoutingStrategy = 'fallback' | 'cost-optimized' | 'latency-optimized' | 'capability-aware';
|
|
4
4
|
export interface ProviderEntry {
|
|
5
5
|
name: string;
|
package/dist/router.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AACrE,OAAO,EAEN,KAAK,oBAAoB,EAEzB,
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AACrE,OAAO,EAEN,KAAK,oBAAoB,EAEzB,YAAY,EAEZ,MAAM,iBAAiB,CAAA;AAKxB,MAAM,MAAM,eAAe,GACxB,UAAU,GACV,gBAAgB,GAChB,mBAAmB,GACnB,kBAAkB,CAAA;AAErB,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB;AAED,MAAM,WAAW,mBAAmB;IACnC,WAAW,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAChD,YAAY,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IACjD,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B,QAAQ,EAAE,eAAe,CAAA;IACzB,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,cAAc,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAA;CAC/C;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAC1D,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,YAAY,CAAA;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAA;CAClC;AAoDD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAgQ3E"}
|
package/dist/security.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,UAAU,EAAqC,MAAM,iBAAiB,CAAA;AAKjG,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,kBAAkB,GAAG,WAAW,GAAG,iBAAiB,GAAG,iBAAiB,CAAA;IAC9E,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAA;CACnC;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,OAAO,CAAA;IACb,UAAU,EAAE,iBAAiB,EAAE,CAAA;CAC/B;AAED,MAAM,WAAW,wBAAwB;IACxC,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,CAAC,CAAA;IACpE,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAA;CACpD;AAuID,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,UAAU,GAAG,cAAc,GAAG,YAAY,CAAA;AAEtF,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,kBAAkB,CAAA;IACzB,aAAa,EAAE,MAAM,EAAE,CAAA;CACvB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CA4BlE;AAQD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,CASvE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,CASjE;AAoCD,wBAAgB,aAAa,CAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,CAAC,GAClE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,iBAAiB,EAAE,CAAA;CAAE,CAYlD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,iBAAiB,EAAE,CAa1F;AAwDD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,UAAU,EAAqC,MAAM,iBAAiB,CAAA;AAKjG,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,kBAAkB,GAAG,WAAW,GAAG,iBAAiB,GAAG,iBAAiB,CAAA;IAC9E,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAA;CACnC;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,OAAO,CAAA;IACb,UAAU,EAAE,iBAAiB,EAAE,CAAA;CAC/B;AAED,MAAM,WAAW,wBAAwB;IACxC,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,CAAC,CAAA;IACpE,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAA;CACpD;AAuID,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,UAAU,GAAG,cAAc,GAAG,YAAY,CAAA;AAEtF,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,kBAAkB,CAAA;IACzB,aAAa,EAAE,MAAM,EAAE,CAAA;CACvB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CA4BlE;AAQD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,CASvE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,CASjE;AAoCD,wBAAgB,aAAa,CAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,CAAC,GAClE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,iBAAiB,EAAE,CAAA;CAAE,CAYlD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,iBAAiB,EAAE,CAa1F;AAwDD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,UAAU,CA+B/E"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elsium-ai/gateway",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Multi-provider LLM gateway for ElsiumAI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Eric Utrera <ebutrera9103@gmail.com>",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dev": "bun --watch src/index.ts"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@elsium-ai/core": "^0.
|
|
29
|
+
"@elsium-ai/core": "^0.3.0",
|
|
30
30
|
"zod": "^3.24.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|