@llmops/gateway 0.2.6 → 0.2.7-beta.2

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/index.cjs CHANGED
@@ -4304,8 +4304,57 @@ function _instanceof(cls, params = {}) {
4304
4304
  return inst;
4305
4305
  }
4306
4306
 
4307
+ //#endregion
4308
+ //#region src/providers/providerIdMapping.ts
4309
+ /**
4310
+ * Provider ID mapping between models.dev and Portkey gateway.
4311
+ *
4312
+ * models.dev uses different provider IDs than Portkey for some providers.
4313
+ * This mapping allows converting from models.dev IDs to Portkey gateway IDs.
4314
+ *
4315
+ * Key: models.dev provider ID
4316
+ * Value: Portkey gateway provider ID
4317
+ */
4318
+ const MODELS_DEV_TO_PORTKEY_PROVIDER_MAP = {
4319
+ "azure-cognitive-services": "azure-ai",
4320
+ azure: "azure-openai"
4321
+ };
4322
+ /**
4323
+ * Reverse mapping from Portkey to models.dev
4324
+ */
4325
+ const PORTKEY_TO_MODELS_DEV_PROVIDER_MAP = Object.fromEntries(Object.entries(MODELS_DEV_TO_PORTKEY_PROVIDER_MAP).map(([k, v]) => [v, k]));
4326
+ /**
4327
+ * Get the Portkey gateway provider ID for a given models.dev provider ID.
4328
+ * Returns the original ID if no mapping exists.
4329
+ *
4330
+ * @param modelsDevProviderId - The provider ID from models.dev
4331
+ * @returns The corresponding Portkey gateway provider ID
4332
+ */
4333
+ function getPortkeyProviderId(modelsDevProviderId) {
4334
+ return MODELS_DEV_TO_PORTKEY_PROVIDER_MAP[modelsDevProviderId] ?? modelsDevProviderId;
4335
+ }
4336
+ /**
4337
+ * Get the models.dev provider ID for a given Portkey gateway provider ID.
4338
+ * Returns the original ID if no mapping exists.
4339
+ *
4340
+ * @param portkeyProviderId - The provider ID from Portkey gateway
4341
+ * @returns The corresponding models.dev provider ID
4342
+ */
4343
+ function getModelsDevProviderId(portkeyProviderId) {
4344
+ return PORTKEY_TO_MODELS_DEV_PROVIDER_MAP[portkeyProviderId] ?? portkeyProviderId;
4345
+ }
4346
+
4307
4347
  //#endregion
4308
4348
  //#region src/middlewares/requestValidator/schema/config.ts
4349
+ /**
4350
+ * Check if a provider ID is valid.
4351
+ * Accepts both Portkey provider IDs and models.dev provider IDs that can be mapped.
4352
+ */
4353
+ function isValidProvider$1(value) {
4354
+ if (VALID_PROVIDERS.includes(value)) return true;
4355
+ const mappedId = getPortkeyProviderId(value);
4356
+ return VALID_PROVIDERS.includes(mappedId);
4357
+ }
4309
4358
  const configSchema = object({
4310
4359
  strategy: object({
4311
4360
  mode: string().refine((value) => [
@@ -4321,7 +4370,7 @@ const configSchema = object({
4321
4370
  })).optional(),
4322
4371
  default: string().optional()
4323
4372
  }).optional(),
4324
- provider: string().refine((value) => VALID_PROVIDERS.includes(value), { message: `Invalid 'provider' value. Must be one of: ${VALID_PROVIDERS.join(", ")}` }).optional(),
4373
+ provider: string().refine((value) => isValidProvider$1(value), { message: `Invalid 'provider' value. Must be one of: ${VALID_PROVIDERS.join(", ")}` }).optional(),
4325
4374
  api_key: string().optional(),
4326
4375
  aws_secret_access_key: string().optional(),
4327
4376
  aws_access_key_id: string().optional(),
@@ -4351,8 +4400,17 @@ const configSchema = object({
4351
4400
  vertex_service_account_json: object({}).catchall(string()).optional(),
4352
4401
  openai_project: string().optional(),
4353
4402
  openai_organization: string().optional(),
4403
+ azure_resource_name: string().optional(),
4404
+ azure_deployment_id: string().optional(),
4405
+ azure_api_version: string().optional(),
4354
4406
  azure_model_name: string().optional(),
4355
4407
  azure_auth_mode: string().optional(),
4408
+ azure_ad_token: string().optional(),
4409
+ azure_managed_client_id: string().optional(),
4410
+ azure_workload_client_id: string().optional(),
4411
+ azure_entra_client_id: string().optional(),
4412
+ azure_entra_client_secret: string().optional(),
4413
+ azure_entra_tenant_id: string().optional(),
4356
4414
  strict_open_ai_compliance: boolean().optional()
4357
4415
  }).refine((value) => {
4358
4416
  const hasProviderApiKey = value.provider !== void 0 && value.api_key !== void 0;
@@ -4447,6 +4505,15 @@ const Environment = (c) => {
4447
4505
 
4448
4506
  //#endregion
4449
4507
  //#region src/middlewares/requestValidator/index.ts
4508
+ /**
4509
+ * Check if a provider ID is valid.
4510
+ * Accepts both Portkey provider IDs and models.dev provider IDs that can be mapped.
4511
+ */
4512
+ function isValidProvider(value) {
4513
+ if (VALID_PROVIDERS.includes(value)) return true;
4514
+ const mappedId = getPortkeyProviderId(value);
4515
+ return VALID_PROVIDERS.includes(mappedId);
4516
+ }
4450
4517
  const VALIDATION_PATTERNS = {
4451
4518
  CONTROL_CHARS: /[\x00-\x1F\x7F]/,
4452
4519
  SUSPICIOUS_CHARS: /[\s<>{}|\\^`]/,
@@ -4552,7 +4619,7 @@ const requestValidator = (c, next) => {
4552
4619
  status: 400,
4553
4620
  headers: { "content-type": "application/json" }
4554
4621
  });
4555
- if (requestHeaders[`x-${POWERED_BY}-provider`] && !VALID_PROVIDERS.includes(requestHeaders[`x-${POWERED_BY}-provider`])) return new Response(JSON.stringify({
4622
+ if (requestHeaders[`x-${POWERED_BY}-provider`] && !isValidProvider(requestHeaders[`x-${POWERED_BY}-provider`])) return new Response(JSON.stringify({
4556
4623
  status: "failure",
4557
4624
  message: `Invalid provider passed`
4558
4625
  }), {
@@ -10045,8 +10112,140 @@ function convertKeysToCamelCase(obj, parentKeysToPreserve = []) {
10045
10112
  }
10046
10113
  }
10047
10114
 
10115
+ //#endregion
10116
+ //#region src/shared/utils/logger.ts
10117
+ /**
10118
+ * @file src/utils/logger.ts
10119
+ * Configurable logger utility for MCP Gateway
10120
+ */
10121
+ let LogLevel = /* @__PURE__ */ function(LogLevel$1) {
10122
+ LogLevel$1[LogLevel$1["ERROR"] = 0] = "ERROR";
10123
+ LogLevel$1[LogLevel$1["CRITICAL"] = 1] = "CRITICAL";
10124
+ LogLevel$1[LogLevel$1["WARN"] = 2] = "WARN";
10125
+ LogLevel$1[LogLevel$1["INFO"] = 3] = "INFO";
10126
+ LogLevel$1[LogLevel$1["DEBUG"] = 4] = "DEBUG";
10127
+ return LogLevel$1;
10128
+ }({});
10129
+ var Logger = class Logger {
10130
+ config;
10131
+ colors = {
10132
+ error: "\x1B[31m",
10133
+ critical: "\x1B[35m",
10134
+ warn: "\x1B[33m",
10135
+ info: "\x1B[36m",
10136
+ debug: "\x1B[37m",
10137
+ reset: "\x1B[0m"
10138
+ };
10139
+ constructor(config$1) {
10140
+ this.config = {
10141
+ timestamp: true,
10142
+ colors: true,
10143
+ ...config$1
10144
+ };
10145
+ }
10146
+ formatMessage(level, message) {
10147
+ const parts = [];
10148
+ if (this.config.timestamp) parts.push(`[${(/* @__PURE__ */ new Date()).toISOString()}]`);
10149
+ if (this.config.prefix) parts.push(`[${this.config.prefix}]`);
10150
+ parts.push(`[${level.toUpperCase()}]`);
10151
+ parts.push(message);
10152
+ return parts.join(" ");
10153
+ }
10154
+ log(level, levelName, message, data) {
10155
+ if (level > this.config.level) return;
10156
+ const formattedMessage = this.formatMessage(levelName, message);
10157
+ const color = this.config.colors ? this.colors[levelName] : "";
10158
+ const reset = this.config.colors ? this.colors.reset : "";
10159
+ if (data !== void 0) console.log(`${color}${formattedMessage}${reset}`, data);
10160
+ else console.log(`${color}${formattedMessage}${reset}`);
10161
+ }
10162
+ error(message, error) {
10163
+ if (error instanceof Error) {
10164
+ this.log(LogLevel.ERROR, "error", `${message}: ${error.message}`);
10165
+ if (this.config.level >= LogLevel.DEBUG) console.error(error.stack);
10166
+ } else if (error) this.log(LogLevel.ERROR, "error", message, error);
10167
+ else this.log(LogLevel.ERROR, "error", message);
10168
+ }
10169
+ critical(message, data) {
10170
+ this.log(LogLevel.CRITICAL, "critical", message, data);
10171
+ }
10172
+ warn(message, data) {
10173
+ this.log(LogLevel.WARN, "warn", message, data);
10174
+ }
10175
+ info(message, data) {
10176
+ this.log(LogLevel.INFO, "info", message, data);
10177
+ }
10178
+ debug(message, data) {
10179
+ this.log(LogLevel.DEBUG, "debug", message, data);
10180
+ }
10181
+ createChild(prefix) {
10182
+ return new Logger({
10183
+ ...this.config,
10184
+ prefix: this.config.prefix ? `${this.config.prefix}:${prefix}` : prefix
10185
+ });
10186
+ }
10187
+ };
10188
+ const defaultConfig = {
10189
+ level: process.env.LOG_LEVEL ? LogLevel[process.env.LOG_LEVEL.toUpperCase()] || LogLevel.ERROR : process.env.NODE_ENV === "production" ? LogLevel.ERROR : LogLevel.INFO,
10190
+ timestamp: process.env.LOG_TIMESTAMP !== "false",
10191
+ colors: process.env.LOG_COLORS !== "false" && process.env.NODE_ENV !== "production"
10192
+ };
10193
+ const logger$7 = new Logger(defaultConfig);
10194
+ function createLogger(prefix) {
10195
+ return logger$7.createChild(prefix);
10196
+ }
10197
+
10048
10198
  //#endregion
10049
10199
  //#region src/handlers/retryHandler.ts
10200
+ const logger$6 = createLogger("ProviderRequest");
10201
+ /**
10202
+ * Sanitizes headers by masking sensitive values like API keys and tokens
10203
+ */
10204
+ function sanitizeHeaders(headers) {
10205
+ if (!headers) return {};
10206
+ const sanitized = {};
10207
+ const sensitivePatterns = /^(authorization|x-api-key|api-key|x-.*-key|x-.*-token|x-.*-secret|bearer)$/i;
10208
+ const headersObj = headers instanceof Headers ? Object.fromEntries([...headers]) : Array.isArray(headers) ? Object.fromEntries(headers) : headers;
10209
+ for (const [key, value] of Object.entries(headersObj)) if (sensitivePatterns.test(key)) sanitized[key] = value.length > 12 ? `${value.substring(0, 8)}...****` : "****";
10210
+ else sanitized[key] = value;
10211
+ return sanitized;
10212
+ }
10213
+ /**
10214
+ * Logs the outgoing request to the provider
10215
+ */
10216
+ function logProviderRequest(url, options, attempt) {
10217
+ const sanitizedHeaders = sanitizeHeaders(options.headers);
10218
+ let bodyPreview;
10219
+ if (options.body) if (typeof options.body === "string") try {
10220
+ const parsed = JSON.parse(options.body);
10221
+ const bodyStr = JSON.stringify(parsed, null, 2);
10222
+ bodyPreview = bodyStr.length > 2e3 ? `${bodyStr.substring(0, 2e3)}...` : bodyStr;
10223
+ } catch {
10224
+ bodyPreview = options.body.length > 500 ? `${options.body.substring(0, 500)}...` : options.body;
10225
+ }
10226
+ else if (options.body instanceof FormData) bodyPreview = "[FormData]";
10227
+ else if (options.body instanceof ArrayBuffer) bodyPreview = `[ArrayBuffer: ${options.body.byteLength} bytes]`;
10228
+ else if (options.body instanceof ReadableStream) bodyPreview = "[ReadableStream]";
10229
+ else bodyPreview = "[Unknown body type]";
10230
+ logger$6.debug(`Provider Request (attempt ${attempt})`, {
10231
+ url,
10232
+ method: options.method || "GET",
10233
+ headers: sanitizedHeaders,
10234
+ body: bodyPreview
10235
+ });
10236
+ }
10237
+ /**
10238
+ * Logs the provider response
10239
+ */
10240
+ function logProviderResponse(url, response, attempt, durationMs) {
10241
+ logger$6.debug(`Provider Response (attempt ${attempt})`, {
10242
+ url,
10243
+ status: response.status,
10244
+ statusText: response.statusText,
10245
+ durationMs,
10246
+ headers: Object.fromEntries([...response.headers])
10247
+ });
10248
+ }
10050
10249
  async function fetchWithTimeout(url, options, timeout, requestHandler) {
10051
10250
  const controller = new AbortController();
10052
10251
  const timeoutId = setTimeout(() => controller.abort(), timeout);
@@ -10095,10 +10294,14 @@ const retryRequest = async (url, options, retryCount, statusCodesToRetry, timeou
10095
10294
  try {
10096
10295
  await (0, async_retry.default)(async (bail, attempt, rateLimiter) => {
10097
10296
  try {
10297
+ logProviderRequest(url, options, attempt);
10298
+ const fetchStartTime = Date.now();
10098
10299
  let response;
10099
10300
  if (timeout) response = await fetchWithTimeout(url, options, timeout, requestHandler);
10100
10301
  else if (requestHandler) response = await requestHandler();
10101
10302
  else response = await fetch(url, options);
10303
+ const fetchDuration = Date.now() - fetchStartTime;
10304
+ logProviderResponse(url, response, attempt, fetchDuration);
10102
10305
  if (statusCodesToRetry.includes(response.status)) {
10103
10306
  const errorObj = new Error(await response.text());
10104
10307
  errorObj.status = response.status;
@@ -10172,7 +10375,7 @@ const retryRequest = async (url, options, retryCount, statusCodesToRetry, timeou
10172
10375
 
10173
10376
  //#endregion
10174
10377
  //#region package.json
10175
- var version = "0.2.6";
10378
+ var version = "0.2.7-beta.2";
10176
10379
 
10177
10380
  //#endregion
10178
10381
  //#region src/providers/bytez/api.ts
@@ -17090,7 +17293,7 @@ const transformToProviderRequest = (provider, params, requestBody, fn, requestHe
17090
17293
  if (fn === "createFinetune" && [AZURE_OPEN_AI, FIREWORKS_AI].includes(provider)) return transformToProviderRequestBody(provider, requestBody, requestHeaders, providerOptions, fn);
17091
17294
  if (requestBody instanceof FormData || requestBody instanceof ArrayBuffer) return requestBody;
17092
17295
  if (fn === "proxy") return params;
17093
- const providerAPIConfig = providers_default[provider].api;
17296
+ const providerAPIConfig = providers_default[getPortkeyProviderId(provider)].api;
17094
17297
  if (providerAPIConfig.transformToFormData && providerAPIConfig.transformToFormData({ gatewayRequestBody: params })) return transformToProviderRequestFormData(provider, params, fn, providerOptions);
17095
17298
  return transformToProviderRequestJSON(provider, params, fn, providerOptions);
17096
17299
  };
@@ -30051,7 +30254,7 @@ async function responseHandler(c, response, streamingMode, providerOptions, resp
30051
30254
  let responseTransformerFunction;
30052
30255
  const responseContentType = response.headers?.get("content-type");
30053
30256
  const isSuccessStatusCode = [200, 246].includes(response.status);
30054
- const provider = providerOptions.provider;
30257
+ const provider = getPortkeyProviderId(providerOptions.provider);
30055
30258
  const providerConfig = providers_default[provider];
30056
30259
  let providerTransformers = providers_default[provider]?.responseTransforms;
30057
30260
  if (providerConfig?.getConfig) providerTransformers = providerConfig.getConfig({
@@ -30653,7 +30856,8 @@ var PreRequestValidatorService = class {
30653
30856
  var ProviderContext = class {
30654
30857
  constructor(provider) {
30655
30858
  this.provider = provider;
30656
- if (!providers_default[provider]) throw new GatewayError(`Provider ${provider} not found`);
30859
+ this.provider = getPortkeyProviderId(provider);
30860
+ if (!providers_default[this.provider]) throw new GatewayError(`Provider ${provider} not found`);
30657
30861
  }
30658
30862
  get providerConfig() {
30659
30863
  return providers_default[this.provider];
@@ -30925,6 +31129,20 @@ var ResponseService = class {
30925
31129
 
30926
31130
  //#endregion
30927
31131
  //#region src/handlers/handlerUtils.ts
31132
+ /**
31133
+ * Normalizes Azure OpenAI config fields from JSON config format to provider format.
31134
+ * JSON config uses `azure_*` prefix (e.g., `azure_resource_name`) which converts to `azureResourceName`,
31135
+ * but the provider expects `resourceName`, `deploymentId`, etc.
31136
+ * This function recursively normalizes nested targets as well.
31137
+ */
31138
+ function normalizeAzureConfig(config$1) {
31139
+ const normalized = { ...config$1 };
31140
+ if (normalized.azureResourceName && !normalized.resourceName) normalized.resourceName = normalized.azureResourceName;
31141
+ if (normalized.azureDeploymentId && !normalized.deploymentId) normalized.deploymentId = normalized.azureDeploymentId;
31142
+ if (normalized.azureApiVersion && !normalized.apiVersion) normalized.apiVersion = normalized.azureApiVersion;
31143
+ if (Array.isArray(normalized.targets)) normalized.targets = normalized.targets.map((target) => normalizeAzureConfig(target));
31144
+ return normalized;
31145
+ }
30928
31146
  function constructRequestBody(requestContext, providerHeaders) {
30929
31147
  const headerContentType = providerHeaders[HEADER_KEYS.CONTENT_TYPE];
30930
31148
  const requestContentType = requestContext.getHeader(HEADER_KEYS.CONTENT_TYPE);
@@ -31438,7 +31656,7 @@ function constructConfigFromRequestHeaders(requestHeaders) {
31438
31656
  ...oracleConfig
31439
31657
  };
31440
31658
  }
31441
- return convertKeysToCamelCase(parsedConfigJson, [
31659
+ return normalizeAzureConfig(convertKeysToCamelCase(parsedConfigJson, [
31442
31660
  "override_params",
31443
31661
  "params",
31444
31662
  "checks",
@@ -31453,7 +31671,7 @@ function constructConfigFromRequestHeaders(requestHeaders) {
31453
31671
  "integrationDetails",
31454
31672
  "virtualKeyDetails",
31455
31673
  "cb_config"
31456
- ]);
31674
+ ]));
31457
31675
  }
31458
31676
  return {
31459
31677
  provider: requestHeaders[`x-${POWERED_BY}-provider`],
@@ -32041,7 +32259,7 @@ async function realTimeHandler(c) {
32041
32259
  try {
32042
32260
  const requestHeaders = Object.fromEntries(c.req.raw.headers);
32043
32261
  const providerOptions = constructConfigFromRequestHeaders(requestHeaders);
32044
- const apiConfig = providers_default[providerOptions.provider ?? ""].api;
32262
+ const apiConfig = providers_default[getPortkeyProviderId(providerOptions.provider ?? "")].api;
32045
32263
  const url = getURLForOutgoingConnection(apiConfig, providerOptions, c.req.url, c);
32046
32264
  const options = await getOptionsForOutgoingConnection(apiConfig, providerOptions, url, c);
32047
32265
  const sessionOptions = {
@@ -33336,5 +33554,9 @@ app.delete("/v1/*", requestValidator, proxyHandler);
33336
33554
  var src_default = app;
33337
33555
 
33338
33556
  //#endregion
33557
+ exports.MODELS_DEV_TO_PORTKEY_PROVIDER_MAP = MODELS_DEV_TO_PORTKEY_PROVIDER_MAP;
33558
+ exports.PORTKEY_TO_MODELS_DEV_PROVIDER_MAP = PORTKEY_TO_MODELS_DEV_PROVIDER_MAP;
33339
33559
  exports.Providers = providers_default;
33340
- exports.default = src_default;
33560
+ exports.default = src_default;
33561
+ exports.getModelsDevProviderId = getModelsDevProviderId;
33562
+ exports.getPortkeyProviderId = getPortkeyProviderId;
package/dist/index.d.cts CHANGED
@@ -423,6 +423,38 @@ interface ProviderConfigs {
423
423
  }) => any;
424
424
  }
425
425
  //#endregion
426
+ //#region src/providers/providerIdMapping.d.ts
427
+ /**
428
+ * Provider ID mapping between models.dev and Portkey gateway.
429
+ *
430
+ * models.dev uses different provider IDs than Portkey for some providers.
431
+ * This mapping allows converting from models.dev IDs to Portkey gateway IDs.
432
+ *
433
+ * Key: models.dev provider ID
434
+ * Value: Portkey gateway provider ID
435
+ */
436
+ declare const MODELS_DEV_TO_PORTKEY_PROVIDER_MAP: Record<string, string>;
437
+ /**
438
+ * Reverse mapping from Portkey to models.dev
439
+ */
440
+ declare const PORTKEY_TO_MODELS_DEV_PROVIDER_MAP: Record<string, string>;
441
+ /**
442
+ * Get the Portkey gateway provider ID for a given models.dev provider ID.
443
+ * Returns the original ID if no mapping exists.
444
+ *
445
+ * @param modelsDevProviderId - The provider ID from models.dev
446
+ * @returns The corresponding Portkey gateway provider ID
447
+ */
448
+ declare function getPortkeyProviderId(modelsDevProviderId: string): string;
449
+ /**
450
+ * Get the models.dev provider ID for a given Portkey gateway provider ID.
451
+ * Returns the original ID if no mapping exists.
452
+ *
453
+ * @param portkeyProviderId - The provider ID from Portkey gateway
454
+ * @returns The corresponding models.dev provider ID
455
+ */
456
+ declare function getModelsDevProviderId(portkeyProviderId: string): string;
457
+ //#endregion
426
458
  //#region src/providers/index.d.ts
427
459
  declare const Providers: {
428
460
  [key: string]: ProviderConfigs;
@@ -431,4 +463,4 @@ declare const Providers: {
431
463
  //#region src/index.d.ts
432
464
  declare const app: Hono<hono_types0.BlankEnv, hono_types0.BlankSchema, "/">;
433
465
  //#endregion
434
- export { Providers, app as default };
466
+ export { MODELS_DEV_TO_PORTKEY_PROVIDER_MAP, PORTKEY_TO_MODELS_DEV_PROVIDER_MAP, Providers, app as default, getModelsDevProviderId, getPortkeyProviderId };
package/dist/index.d.mts CHANGED
@@ -423,6 +423,38 @@ interface ProviderConfigs {
423
423
  }) => any;
424
424
  }
425
425
  //#endregion
426
+ //#region src/providers/providerIdMapping.d.ts
427
+ /**
428
+ * Provider ID mapping between models.dev and Portkey gateway.
429
+ *
430
+ * models.dev uses different provider IDs than Portkey for some providers.
431
+ * This mapping allows converting from models.dev IDs to Portkey gateway IDs.
432
+ *
433
+ * Key: models.dev provider ID
434
+ * Value: Portkey gateway provider ID
435
+ */
436
+ declare const MODELS_DEV_TO_PORTKEY_PROVIDER_MAP: Record<string, string>;
437
+ /**
438
+ * Reverse mapping from Portkey to models.dev
439
+ */
440
+ declare const PORTKEY_TO_MODELS_DEV_PROVIDER_MAP: Record<string, string>;
441
+ /**
442
+ * Get the Portkey gateway provider ID for a given models.dev provider ID.
443
+ * Returns the original ID if no mapping exists.
444
+ *
445
+ * @param modelsDevProviderId - The provider ID from models.dev
446
+ * @returns The corresponding Portkey gateway provider ID
447
+ */
448
+ declare function getPortkeyProviderId(modelsDevProviderId: string): string;
449
+ /**
450
+ * Get the models.dev provider ID for a given Portkey gateway provider ID.
451
+ * Returns the original ID if no mapping exists.
452
+ *
453
+ * @param portkeyProviderId - The provider ID from Portkey gateway
454
+ * @returns The corresponding models.dev provider ID
455
+ */
456
+ declare function getModelsDevProviderId(portkeyProviderId: string): string;
457
+ //#endregion
426
458
  //#region src/providers/index.d.ts
427
459
  declare const Providers: {
428
460
  [key: string]: ProviderConfigs;
@@ -431,4 +463,4 @@ declare const Providers: {
431
463
  //#region src/index.d.ts
432
464
  declare const app: Hono<hono_types0.BlankEnv, hono_types0.BlankSchema, "/">;
433
465
  //#endregion
434
- export { Providers, app as default };
466
+ export { MODELS_DEV_TO_PORTKEY_PROVIDER_MAP, PORTKEY_TO_MODELS_DEV_PROVIDER_MAP, Providers, app as default, getModelsDevProviderId, getPortkeyProviderId };
package/dist/index.mjs CHANGED
@@ -4277,8 +4277,57 @@ function _instanceof(cls, params = {}) {
4277
4277
  return inst;
4278
4278
  }
4279
4279
 
4280
+ //#endregion
4281
+ //#region src/providers/providerIdMapping.ts
4282
+ /**
4283
+ * Provider ID mapping between models.dev and Portkey gateway.
4284
+ *
4285
+ * models.dev uses different provider IDs than Portkey for some providers.
4286
+ * This mapping allows converting from models.dev IDs to Portkey gateway IDs.
4287
+ *
4288
+ * Key: models.dev provider ID
4289
+ * Value: Portkey gateway provider ID
4290
+ */
4291
+ const MODELS_DEV_TO_PORTKEY_PROVIDER_MAP = {
4292
+ "azure-cognitive-services": "azure-ai",
4293
+ azure: "azure-openai"
4294
+ };
4295
+ /**
4296
+ * Reverse mapping from Portkey to models.dev
4297
+ */
4298
+ const PORTKEY_TO_MODELS_DEV_PROVIDER_MAP = Object.fromEntries(Object.entries(MODELS_DEV_TO_PORTKEY_PROVIDER_MAP).map(([k, v]) => [v, k]));
4299
+ /**
4300
+ * Get the Portkey gateway provider ID for a given models.dev provider ID.
4301
+ * Returns the original ID if no mapping exists.
4302
+ *
4303
+ * @param modelsDevProviderId - The provider ID from models.dev
4304
+ * @returns The corresponding Portkey gateway provider ID
4305
+ */
4306
+ function getPortkeyProviderId(modelsDevProviderId) {
4307
+ return MODELS_DEV_TO_PORTKEY_PROVIDER_MAP[modelsDevProviderId] ?? modelsDevProviderId;
4308
+ }
4309
+ /**
4310
+ * Get the models.dev provider ID for a given Portkey gateway provider ID.
4311
+ * Returns the original ID if no mapping exists.
4312
+ *
4313
+ * @param portkeyProviderId - The provider ID from Portkey gateway
4314
+ * @returns The corresponding models.dev provider ID
4315
+ */
4316
+ function getModelsDevProviderId(portkeyProviderId) {
4317
+ return PORTKEY_TO_MODELS_DEV_PROVIDER_MAP[portkeyProviderId] ?? portkeyProviderId;
4318
+ }
4319
+
4280
4320
  //#endregion
4281
4321
  //#region src/middlewares/requestValidator/schema/config.ts
4322
+ /**
4323
+ * Check if a provider ID is valid.
4324
+ * Accepts both Portkey provider IDs and models.dev provider IDs that can be mapped.
4325
+ */
4326
+ function isValidProvider$1(value) {
4327
+ if (VALID_PROVIDERS.includes(value)) return true;
4328
+ const mappedId = getPortkeyProviderId(value);
4329
+ return VALID_PROVIDERS.includes(mappedId);
4330
+ }
4282
4331
  const configSchema = object({
4283
4332
  strategy: object({
4284
4333
  mode: string().refine((value) => [
@@ -4294,7 +4343,7 @@ const configSchema = object({
4294
4343
  })).optional(),
4295
4344
  default: string().optional()
4296
4345
  }).optional(),
4297
- provider: string().refine((value) => VALID_PROVIDERS.includes(value), { message: `Invalid 'provider' value. Must be one of: ${VALID_PROVIDERS.join(", ")}` }).optional(),
4346
+ provider: string().refine((value) => isValidProvider$1(value), { message: `Invalid 'provider' value. Must be one of: ${VALID_PROVIDERS.join(", ")}` }).optional(),
4298
4347
  api_key: string().optional(),
4299
4348
  aws_secret_access_key: string().optional(),
4300
4349
  aws_access_key_id: string().optional(),
@@ -4324,8 +4373,17 @@ const configSchema = object({
4324
4373
  vertex_service_account_json: object({}).catchall(string()).optional(),
4325
4374
  openai_project: string().optional(),
4326
4375
  openai_organization: string().optional(),
4376
+ azure_resource_name: string().optional(),
4377
+ azure_deployment_id: string().optional(),
4378
+ azure_api_version: string().optional(),
4327
4379
  azure_model_name: string().optional(),
4328
4380
  azure_auth_mode: string().optional(),
4381
+ azure_ad_token: string().optional(),
4382
+ azure_managed_client_id: string().optional(),
4383
+ azure_workload_client_id: string().optional(),
4384
+ azure_entra_client_id: string().optional(),
4385
+ azure_entra_client_secret: string().optional(),
4386
+ azure_entra_tenant_id: string().optional(),
4329
4387
  strict_open_ai_compliance: boolean().optional()
4330
4388
  }).refine((value) => {
4331
4389
  const hasProviderApiKey = value.provider !== void 0 && value.api_key !== void 0;
@@ -4420,6 +4478,15 @@ const Environment = (c) => {
4420
4478
 
4421
4479
  //#endregion
4422
4480
  //#region src/middlewares/requestValidator/index.ts
4481
+ /**
4482
+ * Check if a provider ID is valid.
4483
+ * Accepts both Portkey provider IDs and models.dev provider IDs that can be mapped.
4484
+ */
4485
+ function isValidProvider(value) {
4486
+ if (VALID_PROVIDERS.includes(value)) return true;
4487
+ const mappedId = getPortkeyProviderId(value);
4488
+ return VALID_PROVIDERS.includes(mappedId);
4489
+ }
4423
4490
  const VALIDATION_PATTERNS = {
4424
4491
  CONTROL_CHARS: /[\x00-\x1F\x7F]/,
4425
4492
  SUSPICIOUS_CHARS: /[\s<>{}|\\^`]/,
@@ -4525,7 +4592,7 @@ const requestValidator = (c, next) => {
4525
4592
  status: 400,
4526
4593
  headers: { "content-type": "application/json" }
4527
4594
  });
4528
- if (requestHeaders[`x-${POWERED_BY}-provider`] && !VALID_PROVIDERS.includes(requestHeaders[`x-${POWERED_BY}-provider`])) return new Response(JSON.stringify({
4595
+ if (requestHeaders[`x-${POWERED_BY}-provider`] && !isValidProvider(requestHeaders[`x-${POWERED_BY}-provider`])) return new Response(JSON.stringify({
4529
4596
  status: "failure",
4530
4597
  message: `Invalid provider passed`
4531
4598
  }), {
@@ -10018,8 +10085,140 @@ function convertKeysToCamelCase(obj, parentKeysToPreserve = []) {
10018
10085
  }
10019
10086
  }
10020
10087
 
10088
+ //#endregion
10089
+ //#region src/shared/utils/logger.ts
10090
+ /**
10091
+ * @file src/utils/logger.ts
10092
+ * Configurable logger utility for MCP Gateway
10093
+ */
10094
+ let LogLevel = /* @__PURE__ */ function(LogLevel$1) {
10095
+ LogLevel$1[LogLevel$1["ERROR"] = 0] = "ERROR";
10096
+ LogLevel$1[LogLevel$1["CRITICAL"] = 1] = "CRITICAL";
10097
+ LogLevel$1[LogLevel$1["WARN"] = 2] = "WARN";
10098
+ LogLevel$1[LogLevel$1["INFO"] = 3] = "INFO";
10099
+ LogLevel$1[LogLevel$1["DEBUG"] = 4] = "DEBUG";
10100
+ return LogLevel$1;
10101
+ }({});
10102
+ var Logger = class Logger {
10103
+ config;
10104
+ colors = {
10105
+ error: "\x1B[31m",
10106
+ critical: "\x1B[35m",
10107
+ warn: "\x1B[33m",
10108
+ info: "\x1B[36m",
10109
+ debug: "\x1B[37m",
10110
+ reset: "\x1B[0m"
10111
+ };
10112
+ constructor(config$1) {
10113
+ this.config = {
10114
+ timestamp: true,
10115
+ colors: true,
10116
+ ...config$1
10117
+ };
10118
+ }
10119
+ formatMessage(level, message) {
10120
+ const parts = [];
10121
+ if (this.config.timestamp) parts.push(`[${(/* @__PURE__ */ new Date()).toISOString()}]`);
10122
+ if (this.config.prefix) parts.push(`[${this.config.prefix}]`);
10123
+ parts.push(`[${level.toUpperCase()}]`);
10124
+ parts.push(message);
10125
+ return parts.join(" ");
10126
+ }
10127
+ log(level, levelName, message, data) {
10128
+ if (level > this.config.level) return;
10129
+ const formattedMessage = this.formatMessage(levelName, message);
10130
+ const color = this.config.colors ? this.colors[levelName] : "";
10131
+ const reset = this.config.colors ? this.colors.reset : "";
10132
+ if (data !== void 0) console.log(`${color}${formattedMessage}${reset}`, data);
10133
+ else console.log(`${color}${formattedMessage}${reset}`);
10134
+ }
10135
+ error(message, error) {
10136
+ if (error instanceof Error) {
10137
+ this.log(LogLevel.ERROR, "error", `${message}: ${error.message}`);
10138
+ if (this.config.level >= LogLevel.DEBUG) console.error(error.stack);
10139
+ } else if (error) this.log(LogLevel.ERROR, "error", message, error);
10140
+ else this.log(LogLevel.ERROR, "error", message);
10141
+ }
10142
+ critical(message, data) {
10143
+ this.log(LogLevel.CRITICAL, "critical", message, data);
10144
+ }
10145
+ warn(message, data) {
10146
+ this.log(LogLevel.WARN, "warn", message, data);
10147
+ }
10148
+ info(message, data) {
10149
+ this.log(LogLevel.INFO, "info", message, data);
10150
+ }
10151
+ debug(message, data) {
10152
+ this.log(LogLevel.DEBUG, "debug", message, data);
10153
+ }
10154
+ createChild(prefix) {
10155
+ return new Logger({
10156
+ ...this.config,
10157
+ prefix: this.config.prefix ? `${this.config.prefix}:${prefix}` : prefix
10158
+ });
10159
+ }
10160
+ };
10161
+ const defaultConfig = {
10162
+ level: process.env.LOG_LEVEL ? LogLevel[process.env.LOG_LEVEL.toUpperCase()] || LogLevel.ERROR : process.env.NODE_ENV === "production" ? LogLevel.ERROR : LogLevel.INFO,
10163
+ timestamp: process.env.LOG_TIMESTAMP !== "false",
10164
+ colors: process.env.LOG_COLORS !== "false" && process.env.NODE_ENV !== "production"
10165
+ };
10166
+ const logger$7 = new Logger(defaultConfig);
10167
+ function createLogger(prefix) {
10168
+ return logger$7.createChild(prefix);
10169
+ }
10170
+
10021
10171
  //#endregion
10022
10172
  //#region src/handlers/retryHandler.ts
10173
+ const logger$6 = createLogger("ProviderRequest");
10174
+ /**
10175
+ * Sanitizes headers by masking sensitive values like API keys and tokens
10176
+ */
10177
+ function sanitizeHeaders(headers) {
10178
+ if (!headers) return {};
10179
+ const sanitized = {};
10180
+ const sensitivePatterns = /^(authorization|x-api-key|api-key|x-.*-key|x-.*-token|x-.*-secret|bearer)$/i;
10181
+ const headersObj = headers instanceof Headers ? Object.fromEntries([...headers]) : Array.isArray(headers) ? Object.fromEntries(headers) : headers;
10182
+ for (const [key, value] of Object.entries(headersObj)) if (sensitivePatterns.test(key)) sanitized[key] = value.length > 12 ? `${value.substring(0, 8)}...****` : "****";
10183
+ else sanitized[key] = value;
10184
+ return sanitized;
10185
+ }
10186
+ /**
10187
+ * Logs the outgoing request to the provider
10188
+ */
10189
+ function logProviderRequest(url, options, attempt) {
10190
+ const sanitizedHeaders = sanitizeHeaders(options.headers);
10191
+ let bodyPreview;
10192
+ if (options.body) if (typeof options.body === "string") try {
10193
+ const parsed = JSON.parse(options.body);
10194
+ const bodyStr = JSON.stringify(parsed, null, 2);
10195
+ bodyPreview = bodyStr.length > 2e3 ? `${bodyStr.substring(0, 2e3)}...` : bodyStr;
10196
+ } catch {
10197
+ bodyPreview = options.body.length > 500 ? `${options.body.substring(0, 500)}...` : options.body;
10198
+ }
10199
+ else if (options.body instanceof FormData) bodyPreview = "[FormData]";
10200
+ else if (options.body instanceof ArrayBuffer) bodyPreview = `[ArrayBuffer: ${options.body.byteLength} bytes]`;
10201
+ else if (options.body instanceof ReadableStream) bodyPreview = "[ReadableStream]";
10202
+ else bodyPreview = "[Unknown body type]";
10203
+ logger$6.debug(`Provider Request (attempt ${attempt})`, {
10204
+ url,
10205
+ method: options.method || "GET",
10206
+ headers: sanitizedHeaders,
10207
+ body: bodyPreview
10208
+ });
10209
+ }
10210
+ /**
10211
+ * Logs the provider response
10212
+ */
10213
+ function logProviderResponse(url, response, attempt, durationMs) {
10214
+ logger$6.debug(`Provider Response (attempt ${attempt})`, {
10215
+ url,
10216
+ status: response.status,
10217
+ statusText: response.statusText,
10218
+ durationMs,
10219
+ headers: Object.fromEntries([...response.headers])
10220
+ });
10221
+ }
10023
10222
  async function fetchWithTimeout(url, options, timeout, requestHandler) {
10024
10223
  const controller = new AbortController();
10025
10224
  const timeoutId = setTimeout(() => controller.abort(), timeout);
@@ -10068,10 +10267,14 @@ const retryRequest = async (url, options, retryCount, statusCodesToRetry, timeou
10068
10267
  try {
10069
10268
  await retry(async (bail, attempt, rateLimiter) => {
10070
10269
  try {
10270
+ logProviderRequest(url, options, attempt);
10271
+ const fetchStartTime = Date.now();
10071
10272
  let response;
10072
10273
  if (timeout) response = await fetchWithTimeout(url, options, timeout, requestHandler);
10073
10274
  else if (requestHandler) response = await requestHandler();
10074
10275
  else response = await fetch(url, options);
10276
+ const fetchDuration = Date.now() - fetchStartTime;
10277
+ logProviderResponse(url, response, attempt, fetchDuration);
10075
10278
  if (statusCodesToRetry.includes(response.status)) {
10076
10279
  const errorObj = new Error(await response.text());
10077
10280
  errorObj.status = response.status;
@@ -10145,7 +10348,7 @@ const retryRequest = async (url, options, retryCount, statusCodesToRetry, timeou
10145
10348
 
10146
10349
  //#endregion
10147
10350
  //#region package.json
10148
- var version = "0.2.6";
10351
+ var version = "0.2.7-beta.2";
10149
10352
 
10150
10353
  //#endregion
10151
10354
  //#region src/providers/bytez/api.ts
@@ -17063,7 +17266,7 @@ const transformToProviderRequest = (provider, params, requestBody, fn, requestHe
17063
17266
  if (fn === "createFinetune" && [AZURE_OPEN_AI, FIREWORKS_AI].includes(provider)) return transformToProviderRequestBody(provider, requestBody, requestHeaders, providerOptions, fn);
17064
17267
  if (requestBody instanceof FormData || requestBody instanceof ArrayBuffer) return requestBody;
17065
17268
  if (fn === "proxy") return params;
17066
- const providerAPIConfig = providers_default[provider].api;
17269
+ const providerAPIConfig = providers_default[getPortkeyProviderId(provider)].api;
17067
17270
  if (providerAPIConfig.transformToFormData && providerAPIConfig.transformToFormData({ gatewayRequestBody: params })) return transformToProviderRequestFormData(provider, params, fn, providerOptions);
17068
17271
  return transformToProviderRequestJSON(provider, params, fn, providerOptions);
17069
17272
  };
@@ -30024,7 +30227,7 @@ async function responseHandler(c, response, streamingMode, providerOptions, resp
30024
30227
  let responseTransformerFunction;
30025
30228
  const responseContentType = response.headers?.get("content-type");
30026
30229
  const isSuccessStatusCode = [200, 246].includes(response.status);
30027
- const provider = providerOptions.provider;
30230
+ const provider = getPortkeyProviderId(providerOptions.provider);
30028
30231
  const providerConfig = providers_default[provider];
30029
30232
  let providerTransformers = providers_default[provider]?.responseTransforms;
30030
30233
  if (providerConfig?.getConfig) providerTransformers = providerConfig.getConfig({
@@ -30626,7 +30829,8 @@ var PreRequestValidatorService = class {
30626
30829
  var ProviderContext = class {
30627
30830
  constructor(provider) {
30628
30831
  this.provider = provider;
30629
- if (!providers_default[provider]) throw new GatewayError(`Provider ${provider} not found`);
30832
+ this.provider = getPortkeyProviderId(provider);
30833
+ if (!providers_default[this.provider]) throw new GatewayError(`Provider ${provider} not found`);
30630
30834
  }
30631
30835
  get providerConfig() {
30632
30836
  return providers_default[this.provider];
@@ -30898,6 +31102,20 @@ var ResponseService = class {
30898
31102
 
30899
31103
  //#endregion
30900
31104
  //#region src/handlers/handlerUtils.ts
31105
+ /**
31106
+ * Normalizes Azure OpenAI config fields from JSON config format to provider format.
31107
+ * JSON config uses `azure_*` prefix (e.g., `azure_resource_name`) which converts to `azureResourceName`,
31108
+ * but the provider expects `resourceName`, `deploymentId`, etc.
31109
+ * This function recursively normalizes nested targets as well.
31110
+ */
31111
+ function normalizeAzureConfig(config$1) {
31112
+ const normalized = { ...config$1 };
31113
+ if (normalized.azureResourceName && !normalized.resourceName) normalized.resourceName = normalized.azureResourceName;
31114
+ if (normalized.azureDeploymentId && !normalized.deploymentId) normalized.deploymentId = normalized.azureDeploymentId;
31115
+ if (normalized.azureApiVersion && !normalized.apiVersion) normalized.apiVersion = normalized.azureApiVersion;
31116
+ if (Array.isArray(normalized.targets)) normalized.targets = normalized.targets.map((target) => normalizeAzureConfig(target));
31117
+ return normalized;
31118
+ }
30901
31119
  function constructRequestBody(requestContext, providerHeaders) {
30902
31120
  const headerContentType = providerHeaders[HEADER_KEYS.CONTENT_TYPE];
30903
31121
  const requestContentType = requestContext.getHeader(HEADER_KEYS.CONTENT_TYPE);
@@ -31411,7 +31629,7 @@ function constructConfigFromRequestHeaders(requestHeaders) {
31411
31629
  ...oracleConfig
31412
31630
  };
31413
31631
  }
31414
- return convertKeysToCamelCase(parsedConfigJson, [
31632
+ return normalizeAzureConfig(convertKeysToCamelCase(parsedConfigJson, [
31415
31633
  "override_params",
31416
31634
  "params",
31417
31635
  "checks",
@@ -31426,7 +31644,7 @@ function constructConfigFromRequestHeaders(requestHeaders) {
31426
31644
  "integrationDetails",
31427
31645
  "virtualKeyDetails",
31428
31646
  "cb_config"
31429
- ]);
31647
+ ]));
31430
31648
  }
31431
31649
  return {
31432
31650
  provider: requestHeaders[`x-${POWERED_BY}-provider`],
@@ -32014,7 +32232,7 @@ async function realTimeHandler(c) {
32014
32232
  try {
32015
32233
  const requestHeaders = Object.fromEntries(c.req.raw.headers);
32016
32234
  const providerOptions = constructConfigFromRequestHeaders(requestHeaders);
32017
- const apiConfig = providers_default[providerOptions.provider ?? ""].api;
32235
+ const apiConfig = providers_default[getPortkeyProviderId(providerOptions.provider ?? "")].api;
32018
32236
  const url = getURLForOutgoingConnection(apiConfig, providerOptions, c.req.url, c);
32019
32237
  const options = await getOptionsForOutgoingConnection(apiConfig, providerOptions, url, c);
32020
32238
  const sessionOptions = {
@@ -33309,4 +33527,4 @@ app.delete("/v1/*", requestValidator, proxyHandler);
33309
33527
  var src_default = app;
33310
33528
 
33311
33529
  //#endregion
33312
- export { providers_default as Providers, src_default as default };
33530
+ export { MODELS_DEV_TO_PORTKEY_PROVIDER_MAP, PORTKEY_TO_MODELS_DEV_PROVIDER_MAP, providers_default as Providers, src_default as default, getModelsDevProviderId, getPortkeyProviderId };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmops/gateway",
3
- "version": "0.2.6",
3
+ "version": "0.2.7-beta.2",
4
4
  "description": "AI gateway for LLMOps (forked from Portkey)",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",