@elsium-ai/gateway 0.3.0 → 0.4.1

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.
@@ -1 +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"}
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;AAsCD,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,CA+ElE"}
package/dist/gateway.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { CompletionRequest, LLMResponse, Middleware, ProviderConfig, XRayData } from '@elsium-ai/core';
1
+ import type { CompletionRequest, LLMResponse, Middleware, ProviderConfig, StreamMiddleware, XRayData } from '@elsium-ai/core';
2
2
  import { type ElsiumStream } from '@elsium-ai/core';
3
3
  import type { z } from 'zod';
4
4
  import type { LLMProvider } from './provider';
@@ -10,6 +10,7 @@ export interface GatewayConfig {
10
10
  timeout?: number;
11
11
  maxRetries?: number;
12
12
  middleware?: Middleware[];
13
+ streamMiddleware?: StreamMiddleware[];
13
14
  xray?: boolean | {
14
15
  maxHistory?: number;
15
16
  };
@@ -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,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"}
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,gBAAgB,EAChB,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;AAW7C,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,gBAAgB,CAAC,EAAE,gBAAgB,EAAE,CAAA;IACrC,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;AA0BD,wBAAgB,uBAAuB,CACtC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,WAAW,GAC9C,IAAI,CAGN;AAwJD,wBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAkItD"}
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { registerProvider, getProviderFactory, listProviders, registerProviderMe
5
5
  export { createAnthropicProvider } from './providers/anthropic';
6
6
  export { createOpenAIProvider } from './providers/openai';
7
7
  export { createGoogleProvider } from './providers/google';
8
- export { composeMiddleware, loggingMiddleware, costTrackingMiddleware, xrayMiddleware, } from './middleware';
8
+ export { composeMiddleware, composeStreamMiddleware, loggingMiddleware, costTrackingMiddleware, xrayMiddleware, } from './middleware';
9
9
  export type { XRayStore } from './middleware';
10
10
  export { securityMiddleware, detectPromptInjection, detectJailbreak, redactSecrets, checkBlockedPatterns, classifyContent, } from './security';
11
11
  export type { SecurityMiddlewareConfig, SecurityViolation, SecurityResult, DataClassification, ClassificationResult, } from './security';
@@ -15,7 +15,7 @@ export { cacheMiddleware, createInMemoryCache } from './cache';
15
15
  export type { CacheAdapter, CacheStats, CacheMiddlewareConfig } from './cache';
16
16
  export { outputGuardrailMiddleware } from './output-guardrails';
17
17
  export type { OutputGuardrailConfig, OutputGuardrailRule, OutputViolation, } from './output-guardrails';
18
- export { calculateCost, registerPricing } from './pricing';
18
+ export { calculateCost, registerPricing, estimateCost } from './pricing';
19
19
  export { createBatch } from './batch';
20
20
  export type { BatchConfig, BatchResult, BatchResultItem } from './batch';
21
21
  export { createProviderMesh } from './router';
@@ -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,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"}
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,uBAAuB,EACvB,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,YAAY,EAAE,MAAM,WAAW,CAAA;AAGxE,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
@@ -647,6 +647,17 @@ function composeMiddleware(middlewares) {
647
647
  return dispatch(0);
648
648
  };
649
649
  }
650
+ function composeStreamMiddleware(middlewares) {
651
+ return (ctx, source, finalNext) => {
652
+ function dispatch(i, currentCtx, currentSource) {
653
+ if (i >= middlewares.length) {
654
+ return finalNext(currentCtx, currentSource);
655
+ }
656
+ return middlewares[i](currentCtx, currentSource, (c, s) => dispatch(i + 1, c, s));
657
+ }
658
+ return dispatch(0, ctx, source);
659
+ };
660
+ }
650
661
  function loggingMiddleware(logger) {
651
662
  const log3 = logger ?? createLogger({ level: "info" });
652
663
  return async (ctx, next) => {
@@ -851,6 +862,12 @@ function calculateCost(model, usage) {
851
862
  function registerPricing(model, pricing) {
852
863
  PRICING[model] = pricing;
853
864
  }
865
+ function estimateCost(model, tokenCount) {
866
+ const pricing = PRICING[resolveModelName(model)];
867
+ if (!pricing)
868
+ return 0;
869
+ return tokenCount / 1e6 * pricing.inputPerMillion;
870
+ }
854
871
 
855
872
  // src/providers/anthropic.ts
856
873
  var DEFAULT_BASE_URL = "https://api.anthropic.com";
@@ -994,6 +1011,52 @@ function createAnthropicProvider(config) {
994
1011
  input_schema: t.inputSchema
995
1012
  }));
996
1013
  }
1014
+ function buildOptionalParams(req) {
1015
+ const params = {};
1016
+ if (req.temperature !== undefined)
1017
+ params.temperature = req.temperature;
1018
+ if (req.topP !== undefined)
1019
+ params.top_p = req.topP;
1020
+ if (req.stopSequences?.length)
1021
+ params.stop_sequences = req.stopSequences;
1022
+ return params;
1023
+ }
1024
+ function applyStructuredOutput(body, req, tools) {
1025
+ if (!req.schema)
1026
+ return;
1027
+ const jsonSchema = zodToJsonSchema(req.schema);
1028
+ const structuredTool = {
1029
+ name: "_structured_output",
1030
+ description: "Return structured output matching the required schema",
1031
+ input_schema: jsonSchema
1032
+ };
1033
+ body.tools = [...tools ?? [], structuredTool];
1034
+ body.tool_choice = { type: "tool", name: "_structured_output" };
1035
+ }
1036
+ function buildRequestBody(req) {
1037
+ const { system, messages } = formatMessages(req.messages);
1038
+ const model = req.model ?? "claude-sonnet-4-6";
1039
+ const body = {
1040
+ model,
1041
+ messages,
1042
+ max_tokens: req.maxTokens ?? DEFAULT_MAX_TOKENS,
1043
+ ...system || req.system ? { system: req.system ?? system } : {},
1044
+ ...buildOptionalParams(req),
1045
+ ...buildSeedMetadata(req)
1046
+ };
1047
+ const tools = formatTools(req.tools);
1048
+ if (tools)
1049
+ body.tools = tools;
1050
+ applyStructuredOutput(body, req, tools);
1051
+ return body;
1052
+ }
1053
+ function executeWithTimeout(fn, reqSignal) {
1054
+ const controller = new AbortController;
1055
+ const timer = setTimeout(() => controller.abort(), timeout);
1056
+ const signals = [controller.signal, reqSignal].filter(Boolean);
1057
+ const mergedSignal = signals.length > 1 ? AbortSignal.any(signals) : signals[0];
1058
+ return fn(mergedSignal).finally(() => clearTimeout(timer));
1059
+ }
997
1060
  function extractContentBlocks(content) {
998
1061
  const toolCalls = [];
999
1062
  const textParts = [];
@@ -1045,44 +1108,12 @@ function createAnthropicProvider(config) {
1045
1108
  authStyle: "x-api-key"
1046
1109
  },
1047
1110
  async complete(req) {
1048
- const { system, messages } = formatMessages(req.messages);
1049
- const model = req.model ?? "claude-sonnet-4-6";
1050
- const body = {
1051
- model,
1052
- messages,
1053
- max_tokens: req.maxTokens ?? DEFAULT_MAX_TOKENS,
1054
- ...system || req.system ? { system: req.system ?? system } : {},
1055
- ...req.temperature !== undefined ? { temperature: req.temperature } : {},
1056
- ...req.topP !== undefined ? { top_p: req.topP } : {},
1057
- ...req.stopSequences?.length ? { stop_sequences: req.stopSequences } : {},
1058
- ...buildSeedMetadata(req)
1059
- };
1060
- const tools = formatTools(req.tools);
1061
- if (tools)
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
- }
1111
+ const body = buildRequestBody(req);
1073
1112
  const startTime = performance.now();
1074
- const raw = await retry(async () => {
1075
- const controller = new AbortController;
1076
- const timer = setTimeout(() => controller.abort(), timeout);
1077
- try {
1078
- const signals = [controller.signal, req.signal].filter(Boolean);
1079
- const mergedSignal = signals.length > 1 ? AbortSignal.any(signals) : signals[0];
1080
- const resp = await request("/messages", body, mergedSignal);
1081
- return await resp.json();
1082
- } finally {
1083
- clearTimeout(timer);
1084
- }
1085
- }, {
1113
+ const raw = await retry(() => executeWithTimeout(async (signal) => {
1114
+ const resp = await request("/messages", body, signal);
1115
+ return await resp.json();
1116
+ }, req.signal), {
1086
1117
  maxRetries,
1087
1118
  baseDelayMs: 1000,
1088
1119
  shouldRetry: (e) => e instanceof ElsiumError && e.retryable
@@ -1091,29 +1122,12 @@ function createAnthropicProvider(config) {
1091
1122
  return parseResponse(raw, latencyMs);
1092
1123
  },
1093
1124
  stream(req) {
1094
- const { system, messages } = formatMessages(req.messages);
1095
- const model = req.model ?? "claude-sonnet-4-6";
1096
- const body = {
1097
- model,
1098
- messages,
1099
- max_tokens: req.maxTokens ?? DEFAULT_MAX_TOKENS,
1100
- stream: true,
1101
- ...system || req.system ? { system: req.system ?? system } : {},
1102
- ...req.temperature !== undefined ? { temperature: req.temperature } : {},
1103
- ...req.topP !== undefined ? { top_p: req.topP } : {},
1104
- ...req.stopSequences?.length ? { stop_sequences: req.stopSequences } : {},
1105
- ...buildSeedMetadata(req)
1106
- };
1107
- const tools = formatTools(req.tools);
1108
- if (tools)
1109
- body.tools = tools;
1125
+ const body = buildRequestBody(req);
1126
+ body.stream = true;
1127
+ const model = body.model ?? "claude-sonnet-4-6";
1110
1128
  return createStream(async (emit) => {
1111
- const controller = new AbortController;
1112
- const timer = setTimeout(() => controller.abort(), timeout);
1113
- try {
1114
- const signals = [controller.signal, req.signal].filter(Boolean);
1115
- const mergedSignal = signals.length > 1 ? AbortSignal.any(signals) : signals[0];
1116
- const resp = await request("/messages", body, mergedSignal);
1129
+ await executeWithTimeout(async (signal) => {
1130
+ const resp = await request("/messages", body, signal);
1117
1131
  if (!resp.body)
1118
1132
  throw new ElsiumError({
1119
1133
  code: "STREAM_ERROR",
@@ -1122,9 +1136,7 @@ function createAnthropicProvider(config) {
1122
1136
  retryable: false
1123
1137
  });
1124
1138
  await processAnthropicSSEStream(resp.body, model, emit);
1125
- } finally {
1126
- clearTimeout(timer);
1127
- }
1139
+ }, req.signal);
1128
1140
  });
1129
1141
  },
1130
1142
  async listModels() {
@@ -1278,31 +1290,38 @@ function createGoogleProvider(config) {
1278
1290
  }
1279
1291
  return { role, parts };
1280
1292
  }
1293
+ function convertGeminiImagePart(p) {
1294
+ if (p.source.type === "base64") {
1295
+ return { inlineData: { mimeType: p.source.mediaType, data: p.source.data } };
1296
+ }
1297
+ return { fileData: { mimeType: "image/jpeg", fileUri: p.source.url } };
1298
+ }
1299
+ function convertGeminiMediaPart(p) {
1300
+ if (p.source.type === "base64") {
1301
+ return { inlineData: { mimeType: p.source.mediaType, data: p.source.data } };
1302
+ }
1303
+ const urlSource = p.source;
1304
+ return { fileData: { mimeType: "application/octet-stream", fileUri: urlSource.url } };
1305
+ }
1306
+ function convertGeminiContentPart(p) {
1307
+ if (p.type === "text") {
1308
+ return { text: p.text };
1309
+ }
1310
+ if (p.type === "image") {
1311
+ return convertGeminiImagePart(p);
1312
+ }
1313
+ if (p.type === "audio" || p.type === "document") {
1314
+ return convertGeminiMediaPart(p);
1315
+ }
1316
+ return null;
1317
+ }
1281
1318
  function formatGeminiMultipartContent(msg, role) {
1319
+ const content = msg.content;
1282
1320
  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
- }
1321
+ for (const p of content) {
1322
+ const converted = convertGeminiContentPart(p);
1323
+ if (converted)
1324
+ parts.push(converted);
1306
1325
  }
1307
1326
  return { role, parts };
1308
1327
  }
@@ -1669,40 +1688,48 @@ function createOpenAIProvider(config) {
1669
1688
  }
1670
1689
  return openaiMsg;
1671
1690
  }
1691
+ function convertImagePart(part) {
1692
+ if (part.source.type === "base64") {
1693
+ const url = `data:${part.source.mediaType};base64,${part.source.data}`;
1694
+ return { type: "image_url", image_url: { url } };
1695
+ }
1696
+ return { type: "image_url", image_url: { url: part.source.url } };
1697
+ }
1698
+ function convertAudioPart(part) {
1699
+ if (part.source.type === "base64") {
1700
+ const format = part.source.mediaType.split("/")[1] ?? "wav";
1701
+ return { type: "input_audio", input_audio: { data: part.source.data, format } };
1702
+ }
1703
+ return { type: "text", text: "[audio: url source requires file upload]" };
1704
+ }
1705
+ function convertDocumentPart(part) {
1706
+ if (part.source.type === "base64") {
1707
+ return {
1708
+ type: "text",
1709
+ text: `[document: ${part.source.mediaType} content attached as base64]`
1710
+ };
1711
+ }
1712
+ return { type: "text", text: `[document: ${part.source.url}]` };
1713
+ }
1714
+ function convertContentPart(part) {
1715
+ if (part.type === "text")
1716
+ return { type: "text", text: part.text };
1717
+ if (part.type === "image")
1718
+ return convertImagePart(part);
1719
+ if (part.type === "audio")
1720
+ return convertAudioPart(part);
1721
+ if (part.type === "document")
1722
+ return convertDocumentPart(part);
1723
+ return null;
1724
+ }
1672
1725
  function formatUserContent(msg) {
1673
1726
  if (typeof msg.content === "string")
1674
1727
  return msg.content;
1675
1728
  const parts = [];
1676
1729
  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
- }
1730
+ const converted = convertContentPart(part);
1731
+ if (converted)
1732
+ parts.push(converted);
1706
1733
  }
1707
1734
  return parts;
1708
1735
  }
@@ -1737,6 +1764,49 @@ function createOpenAIProvider(config) {
1737
1764
  }
1738
1765
  }));
1739
1766
  }
1767
+ function buildOptionalParams(req) {
1768
+ const params = {};
1769
+ if (req.temperature !== undefined)
1770
+ params.temperature = req.temperature;
1771
+ if (req.seed !== undefined)
1772
+ params.seed = req.seed;
1773
+ if (req.topP !== undefined)
1774
+ params.top_p = req.topP;
1775
+ if (req.stopSequences?.length)
1776
+ params.stop = req.stopSequences;
1777
+ return params;
1778
+ }
1779
+ function applyResponseFormat(body, req) {
1780
+ if (!req.schema)
1781
+ return;
1782
+ const jsonSchema = zodToJsonSchema(req.schema);
1783
+ body.response_format = {
1784
+ type: "json_schema",
1785
+ json_schema: {
1786
+ name: "structured_output",
1787
+ strict: true,
1788
+ schema: jsonSchema
1789
+ }
1790
+ };
1791
+ }
1792
+ function buildRequestBody(req) {
1793
+ const messages = formatMessages(req.messages);
1794
+ const model = req.model ?? "gpt-4o";
1795
+ if (req.system) {
1796
+ messages.unshift({ role: "system", content: req.system });
1797
+ }
1798
+ const body = {
1799
+ model,
1800
+ messages,
1801
+ max_tokens: req.maxTokens ?? DEFAULT_MAX_TOKENS2,
1802
+ ...buildOptionalParams(req)
1803
+ };
1804
+ const tools = formatTools(req.tools);
1805
+ if (tools)
1806
+ body.tools = tools;
1807
+ applyResponseFormat(body, req);
1808
+ return body;
1809
+ }
1740
1810
  function parseResponse(raw, latencyMs) {
1741
1811
  const traceId = generateTraceId();
1742
1812
  const choice = raw.choices[0];
@@ -1781,34 +1851,7 @@ function createOpenAIProvider(config) {
1781
1851
  authStyle: "bearer"
1782
1852
  },
1783
1853
  async complete(req) {
1784
- const messages = formatMessages(req.messages);
1785
- const model = req.model ?? "gpt-4o";
1786
- if (req.system) {
1787
- messages.unshift({ role: "system", content: req.system });
1788
- }
1789
- const body = {
1790
- model,
1791
- messages,
1792
- max_tokens: req.maxTokens ?? DEFAULT_MAX_TOKENS2,
1793
- ...req.temperature !== undefined ? { temperature: req.temperature } : {},
1794
- ...req.seed !== undefined ? { seed: req.seed } : {},
1795
- ...req.topP !== undefined ? { top_p: req.topP } : {},
1796
- ...req.stopSequences?.length ? { stop: req.stopSequences } : {}
1797
- };
1798
- const tools = formatTools(req.tools);
1799
- if (tools)
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
- }
1854
+ const body = buildRequestBody(req);
1812
1855
  const startTime = performance.now();
1813
1856
  const raw = await retry(async () => {
1814
1857
  const controller = new AbortController;
@@ -1830,25 +1873,10 @@ function createOpenAIProvider(config) {
1830
1873
  return parseResponse(raw, latencyMs);
1831
1874
  },
1832
1875
  stream(req) {
1833
- const messages = formatMessages(req.messages);
1834
- const model = req.model ?? "gpt-4o";
1835
- if (req.system) {
1836
- messages.unshift({ role: "system", content: req.system });
1837
- }
1838
- const body = {
1839
- model,
1840
- messages,
1841
- max_tokens: req.maxTokens ?? DEFAULT_MAX_TOKENS2,
1842
- stream: true,
1843
- stream_options: { include_usage: true },
1844
- ...req.temperature !== undefined ? { temperature: req.temperature } : {},
1845
- ...req.seed !== undefined ? { seed: req.seed } : {},
1846
- ...req.topP !== undefined ? { top_p: req.topP } : {},
1847
- ...req.stopSequences?.length ? { stop: req.stopSequences } : {}
1848
- };
1849
- const tools = formatTools(req.tools);
1850
- if (tools)
1851
- body.tools = tools;
1876
+ const body = buildRequestBody(req);
1877
+ body.stream = true;
1878
+ body.stream_options = { include_usage: true };
1879
+ const model = body.model ?? "gpt-4o";
1852
1880
  return createStream(async (emit) => {
1853
1881
  const controller = new AbortController;
1854
1882
  const timer = setTimeout(() => controller.abort(), timeout);
@@ -1968,15 +1996,33 @@ var PROVIDER_FACTORIES = {
1968
1996
  openai: createOpenAIProvider,
1969
1997
  google: createGoogleProvider
1970
1998
  };
1999
+ registerProviderMetadata("anthropic", {
2000
+ baseUrl: "https://api.anthropic.com/v1/messages",
2001
+ capabilities: ["tools", "vision", "streaming", "system"],
2002
+ authStyle: "x-api-key"
2003
+ });
2004
+ registerProviderMetadata("openai", {
2005
+ baseUrl: "https://api.openai.com/v1/chat/completions",
2006
+ capabilities: ["tools", "vision", "streaming", "system", "json_mode"],
2007
+ authStyle: "bearer"
2008
+ });
2009
+ registerProviderMetadata("google", {
2010
+ baseUrl: "https://generativelanguage.googleapis.com/v1beta/models",
2011
+ capabilities: ["tools", "vision", "streaming", "system"],
2012
+ authStyle: "bearer"
2013
+ });
1971
2014
  function registerProviderFactory(name, factory) {
1972
2015
  PROVIDER_FACTORIES[name] = factory;
2016
+ registerProvider(name, factory);
1973
2017
  }
1974
2018
  function validateGatewayConfig(config) {
1975
- const factory = PROVIDER_FACTORIES[config.provider];
2019
+ const factory = PROVIDER_FACTORIES[config.provider] ?? getProviderFactory(config.provider);
1976
2020
  if (!factory) {
2021
+ const available = [...Object.keys(PROVIDER_FACTORIES), ...listProviders()];
2022
+ const unique = [...new Set(available)];
1977
2023
  throw new ElsiumError({
1978
2024
  code: "CONFIG_ERROR",
1979
- message: `Unknown provider: ${config.provider}. Available: ${Object.keys(PROVIDER_FACTORIES).join(", ")}`,
2025
+ message: `Unknown provider: ${config.provider}. Available: ${unique.join(", ")}`,
1980
2026
  retryable: false
1981
2027
  });
1982
2028
  }
@@ -2054,6 +2100,24 @@ async function accumulateStreamEvents(stream, emit) {
2054
2100
  }
2055
2101
  return { textContent, usage, stopReason, id };
2056
2102
  }
2103
+ function extractFromToolCalls(response) {
2104
+ if (response.stopReason !== "tool_use" || !response.message.toolCalls?.length) {
2105
+ return;
2106
+ }
2107
+ const structuredCall = response.message.toolCalls.find((tc) => tc.name === "_structured_output");
2108
+ return structuredCall?.arguments;
2109
+ }
2110
+ function extractJsonFromText(response) {
2111
+ let text = typeof response.message.content === "string" ? response.message.content : "";
2112
+ text = text.replace(/^```(?:json)?\s*\n?([\s\S]*?)\n?\s*```$/gm, "$1").trim();
2113
+ const jsonMatch = text.match(/(\{[\s\S]*\}|\[[\s\S]*\])/);
2114
+ if (!jsonMatch) {
2115
+ throw ElsiumError.validation("LLM response did not contain valid JSON", {
2116
+ response: text
2117
+ });
2118
+ }
2119
+ return JSON.parse(jsonMatch[0]);
2120
+ }
2057
2121
  function gateway(config) {
2058
2122
  const factory = validateGatewayConfig(config);
2059
2123
  const provider = factory({
@@ -2075,6 +2139,7 @@ function gateway(config) {
2075
2139
  allMiddleware.push(xm);
2076
2140
  }
2077
2141
  const composedMiddleware = allMiddleware.length ? composeMiddleware(allMiddleware) : null;
2142
+ const composedStreamMiddleware = config.streamMiddleware?.length ? composeStreamMiddleware(config.streamMiddleware) : null;
2078
2143
  async function executeWithMiddleware(request) {
2079
2144
  const req = { ...request, model: request.model ?? defaultModel };
2080
2145
  if (!composedMiddleware) {
@@ -2099,11 +2164,11 @@ function gateway(config) {
2099
2164
  validateRequestLimits(request, maxMessages, maxInputTokens);
2100
2165
  const req = { ...request, model: request.model ?? defaultModel };
2101
2166
  if (composedMiddleware) {
2102
- const ctx = buildMiddlewareContext(req, provider.name, defaultModel, request.metadata ?? {});
2167
+ const ctx2 = buildMiddlewareContext(req, provider.name, defaultModel, request.metadata ?? {});
2103
2168
  return createStream(async (emit) => {
2104
- await composedMiddleware(ctx, async (c) => {
2169
+ await composedMiddleware(ctx2, async (c) => {
2105
2170
  const result = await accumulateStreamEvents(provider.stream(c.request), emit);
2106
- const latencyMs = Math.round(performance.now() - ctx.startTime);
2171
+ const latencyMs = Math.round(performance.now() - ctx2.startTime);
2107
2172
  return {
2108
2173
  id: result.id,
2109
2174
  message: { role: "assistant", content: result.textContent },
@@ -2113,12 +2178,21 @@ function gateway(config) {
2113
2178
  provider: provider.name,
2114
2179
  stopReason: result.stopReason,
2115
2180
  latencyMs,
2116
- traceId: ctx.traceId
2181
+ traceId: ctx2.traceId
2117
2182
  };
2118
2183
  });
2119
2184
  });
2120
2185
  }
2121
- return provider.stream(req);
2186
+ const rawStream = provider.stream(req);
2187
+ if (!composedStreamMiddleware)
2188
+ return rawStream;
2189
+ const ctx = buildMiddlewareContext(req, provider.name, defaultModel, request.metadata ?? {});
2190
+ return createStream(async (emit) => {
2191
+ const processed = composedStreamMiddleware(ctx, rawStream, (_c, s) => s);
2192
+ for await (const event of processed) {
2193
+ emit(event);
2194
+ }
2195
+ });
2122
2196
  },
2123
2197
  async generate(request) {
2124
2198
  const { schema, ...rest } = request;
@@ -2135,23 +2209,7 @@ function gateway(config) {
2135
2209
 
2136
2210
  `)
2137
2211
  });
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]);
2154
- }
2212
+ const parsed = extractFromToolCalls(response) ?? extractJsonFromText(response);
2155
2213
  const result = schema.safeParse(parsed);
2156
2214
  if (!result.success) {
2157
2215
  throw ElsiumError.validation("LLM response did not match schema", {
@@ -2219,6 +2277,21 @@ var SECRET_PATTERNS = [
2219
2277
  detail: "API public key detected",
2220
2278
  replacement: "[REDACTED_API_KEY]"
2221
2279
  },
2280
+ {
2281
+ pattern: /\bghp_[a-zA-Z0-9]{36,}\b/g,
2282
+ detail: "GitHub personal access token detected",
2283
+ replacement: "[REDACTED_GITHUB_TOKEN]"
2284
+ },
2285
+ {
2286
+ pattern: /\bgho_[a-zA-Z0-9]{36,}\b/g,
2287
+ detail: "GitHub OAuth token detected",
2288
+ replacement: "[REDACTED_GITHUB_TOKEN]"
2289
+ },
2290
+ {
2291
+ pattern: /\bgithub_pat_[a-zA-Z0-9_]{20,}\b/g,
2292
+ detail: "GitHub fine-grained token detected",
2293
+ replacement: "[REDACTED_GITHUB_TOKEN]"
2294
+ },
2222
2295
  {
2223
2296
  pattern: /\bapi_key[=:]\s*["']?[a-zA-Z0-9_-]{16,}["']?/gi,
2224
2297
  detail: "API key assignment detected",
@@ -2330,6 +2403,7 @@ function redactPatterns(text, patterns) {
2330
2403
  let redacted = text;
2331
2404
  for (const { pattern, detail, replacement } of patterns) {
2332
2405
  const regex = new RegExp(pattern.source, pattern.flags);
2406
+ regex.lastIndex = 0;
2333
2407
  const result = redacted.replace(regex, replacement);
2334
2408
  if (result !== redacted) {
2335
2409
  found.push({ type: "secret_detected", detail, severity: "medium" });
@@ -2361,7 +2435,8 @@ function redactSecrets(text, piiTypes) {
2361
2435
  }
2362
2436
  function checkBlockedPatterns(text, patterns) {
2363
2437
  const violations = [];
2364
- for (const pattern of patterns) {
2438
+ for (const raw of patterns) {
2439
+ const pattern = typeof raw === "string" ? new RegExp(raw, "i") : raw;
2365
2440
  pattern.lastIndex = 0;
2366
2441
  if (pattern.test(text)) {
2367
2442
  violations.push({
@@ -2754,6 +2829,35 @@ function outputGuardrailMiddleware(config) {
2754
2829
  }
2755
2830
  // src/batch.ts
2756
2831
  var log6 = createLogger();
2832
+ function makeCancelledItem(index) {
2833
+ return { index, success: false, error: "Batch cancelled" };
2834
+ }
2835
+ function makeFailedItem(index, error) {
2836
+ return { index, success: false, error };
2837
+ }
2838
+ async function attemptRequest(gateway2, request, retryPerItem) {
2839
+ let lastError;
2840
+ for (let attempt = 0;attempt <= retryPerItem; attempt++) {
2841
+ try {
2842
+ const response = await gateway2.complete(request);
2843
+ return { response };
2844
+ } catch (err2) {
2845
+ lastError = err2 instanceof Error ? err2.message : String(err2);
2846
+ const isRetryable = attempt < retryPerItem && err2 instanceof ElsiumError && err2.retryable;
2847
+ if (!isRetryable)
2848
+ break;
2849
+ }
2850
+ }
2851
+ return { error: lastError };
2852
+ }
2853
+ function cancelRemaining(results, fromIndex, total) {
2854
+ let cancelled = 0;
2855
+ for (let i = fromIndex;i < total; i++) {
2856
+ results[i] = makeCancelledItem(i);
2857
+ cancelled++;
2858
+ }
2859
+ return cancelled;
2860
+ }
2757
2861
  function createBatch(gateway2, config) {
2758
2862
  const concurrency = config?.concurrency ?? 5;
2759
2863
  const retryPerItem = config?.retryPerItem ?? 0;
@@ -2769,40 +2873,24 @@ function createBatch(gateway2, config) {
2769
2873
  const signal = config?.signal;
2770
2874
  async function processItem(index) {
2771
2875
  if (signal?.aborted) {
2772
- results[index] = {
2773
- index,
2774
- success: false,
2775
- error: "Batch cancelled"
2776
- };
2876
+ results[index] = makeCancelledItem(index);
2777
2877
  totalFailed++;
2778
2878
  return;
2779
2879
  }
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
- }
2880
+ const result = await attemptRequest(gateway2, requests[index], retryPerItem);
2881
+ if (result.response) {
2882
+ results[index] = { index, success: true, response: result.response };
2883
+ totalSucceeded++;
2884
+ } else {
2885
+ results[index] = makeFailedItem(index, result.error);
2886
+ totalFailed++;
2794
2887
  }
2795
- results[index] = { index, success: false, error: lastError };
2796
- totalFailed++;
2797
2888
  }
2798
2889
  return new Promise((resolve) => {
2799
2890
  function scheduleNext() {
2800
2891
  while (running < concurrency && nextIndex < requests.length) {
2801
2892
  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
- }
2893
+ totalFailed += cancelRemaining(results, nextIndex, requests.length);
2806
2894
  nextIndex = requests.length;
2807
2895
  break;
2808
2896
  }
@@ -3100,6 +3188,7 @@ export {
3100
3188
  getProviderMetadata,
3101
3189
  getProviderFactory,
3102
3190
  gateway,
3191
+ estimateCost,
3103
3192
  detectPromptInjection,
3104
3193
  detectJailbreak,
3105
3194
  createProviderMesh,
@@ -3110,6 +3199,7 @@ export {
3110
3199
  createBatch,
3111
3200
  createAnthropicProvider,
3112
3201
  costTrackingMiddleware,
3202
+ composeStreamMiddleware,
3113
3203
  composeMiddleware,
3114
3204
  classifyContent,
3115
3205
  checkBlockedPatterns,
@@ -1,5 +1,6 @@
1
- import type { Logger, Middleware, XRayData } from '@elsium-ai/core';
1
+ import type { Logger, Middleware, StreamMiddleware, XRayData } from '@elsium-ai/core';
2
2
  export declare function composeMiddleware(middlewares: Middleware[]): Middleware;
3
+ export declare function composeStreamMiddleware(middlewares: StreamMiddleware[]): StreamMiddleware;
3
4
  export declare function loggingMiddleware(logger?: Logger): Middleware;
4
5
  export declare function costTrackingMiddleware(): Middleware & {
5
6
  getTotalCost(): number;
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEX,MAAM,EACN,UAAU,EAGV,QAAQ,EACR,MAAM,iBAAiB,CAAA;AAIxB,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,UAAU,CAoBvE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAyB7D;AAED,wBAAgB,sBAAsB,IAAI,UAAU,GAAG;IACtD,YAAY,IAAI,MAAM,CAAA;IACtB,cAAc,IAAI,MAAM,CAAA;IACxB,YAAY,IAAI,MAAM,CAAA;IACtB,KAAK,IAAI,IAAI,CAAA;CACb,CAuBA;AAkBD,MAAM,WAAW,SAAS;IACzB,QAAQ,IAAI,QAAQ,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAA;IACvC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACnD,KAAK,IAAI,IAAI,CAAA;CACb;AAkFD,wBAAgB,cAAc,CAAC,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,UAAU,GAAG,SAAS,CA4B5F"}
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEX,MAAM,EACN,UAAU,EAIV,gBAAgB,EAChB,QAAQ,EACR,MAAM,iBAAiB,CAAA;AAIxB,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,UAAU,CAoBvE;AAED,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,CAczF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAyB7D;AAED,wBAAgB,sBAAsB,IAAI,UAAU,GAAG;IACtD,YAAY,IAAI,MAAM,CAAA;IACtB,cAAc,IAAI,MAAM,CAAA;IACxB,YAAY,IAAI,MAAM,CAAA;IACtB,KAAK,IAAI,IAAI,CAAA;CACb,CAuBA;AAkBD,MAAM,WAAW,SAAS;IACzB,QAAQ,IAAI,QAAQ,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAA;IACvC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACnD,KAAK,IAAI,IAAI,CAAA;CACb;AAkFD,wBAAgB,cAAc,CAAC,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,UAAU,GAAG,SAAS,CA4B5F"}
package/dist/pricing.d.ts CHANGED
@@ -2,4 +2,5 @@ import type { CostBreakdown, TokenUsage } from '@elsium-ai/core';
2
2
  import type { ModelPricing } from './provider';
3
3
  export declare function calculateCost(model: string, usage: TokenUsage): CostBreakdown;
4
4
  export declare function registerPricing(model: string, pricing: ModelPricing): void;
5
+ export declare function estimateCost(model: string, tokenCount: number): number;
5
6
  //# sourceMappingURL=pricing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEhE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AA0C9C,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,aAAa,CAwB7E;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI,CAE1E"}
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEhE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AA0C9C,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,aAAa,CAwB7E;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI,CAE1E;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAItE"}
@@ -1 +1 @@
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
+ {"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,CAqV3E"}
@@ -1 +1 @@
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
+ {"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,CAoSxE"}
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EAON,KAAK,cAAc,EAUnB,MAAM,iBAAiB,CAAA;AAExB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAqD9C,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,WAAW,CAwUxE"}
@@ -12,7 +12,7 @@ export interface SecurityMiddlewareConfig {
12
12
  promptInjection?: boolean;
13
13
  secretRedaction?: boolean;
14
14
  jailbreakDetection?: boolean;
15
- blockedPatterns?: RegExp[];
15
+ blockedPatterns?: (string | RegExp)[];
16
16
  piiTypes?: Array<'email' | 'phone' | 'address' | 'passport' | 'all'>;
17
17
  onViolation?: (violation: SecurityViolation) => void;
18
18
  }
@@ -28,6 +28,6 @@ export declare function redactSecrets(text: string, piiTypes?: Array<'email' | '
28
28
  redacted: string;
29
29
  found: SecurityViolation[];
30
30
  };
31
- export declare function checkBlockedPatterns(text: string, patterns: RegExp[]): SecurityViolation[];
31
+ export declare function checkBlockedPatterns(text: string, patterns: (string | RegExp)[]): SecurityViolation[];
32
32
  export declare function securityMiddleware(config: SecurityMiddlewareConfig): Middleware;
33
33
  //# sourceMappingURL=security.d.ts.map
@@ -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,CA+B/E"}
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,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAA;IACrC,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;AAsJD,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;AAqCD,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,CACnC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAC3B,iBAAiB,EAAE,CAcrB;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.0",
3
+ "version": "0.4.1",
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.3.0",
29
+ "@elsium-ai/core": "^0.4.1",
30
30
  "zod": "^3.24.0"
31
31
  },
32
32
  "devDependencies": {