@infersec/conduit 1.98.1 → 1.99.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/cli.js CHANGED
@@ -21381,6 +21381,165 @@ object$1({
21381
21381
  password: RawPasswordSchema
21382
21382
  });
21383
21383
 
21384
+ _enum$1(["delete", "read", "write"]);
21385
+ const ACCOUNT_MANAGEMENT_OPERATION_METADATA = [
21386
+ {
21387
+ category: "read",
21388
+ description: "List inference endpoints in the account.",
21389
+ name: "list_endpoints"
21390
+ },
21391
+ {
21392
+ category: "read",
21393
+ description: "Get full details for a single inference endpoint, including its sources and tool service.",
21394
+ name: "get_endpoint_details"
21395
+ },
21396
+ { category: "write", description: "Create a new inference endpoint.", name: "create_endpoint" },
21397
+ {
21398
+ category: "write",
21399
+ description: "Update an inference endpoint's name, routing method or sources.",
21400
+ name: "update_endpoint"
21401
+ },
21402
+ {
21403
+ category: "write",
21404
+ description: "Enable or disable an inference endpoint.",
21405
+ name: "toggle_endpoint"
21406
+ },
21407
+ { category: "delete", description: "Delete an inference endpoint.", name: "delete_endpoint" },
21408
+ {
21409
+ category: "read",
21410
+ description: "List inference sources in the account.",
21411
+ name: "list_sources"
21412
+ },
21413
+ {
21414
+ category: "read",
21415
+ description: "Get full details for a single inference source.",
21416
+ name: "get_source_details"
21417
+ },
21418
+ { category: "write", description: "Create a new inference source.", name: "create_source" },
21419
+ {
21420
+ category: "write",
21421
+ description: "Update an inference source's name, engine, model or context length.",
21422
+ name: "update_source"
21423
+ },
21424
+ { category: "delete", description: "Delete an inference source.", name: "delete_source" },
21425
+ { category: "read", description: "List models in the account.", name: "list_models" },
21426
+ {
21427
+ category: "read",
21428
+ description: "Get full details for a single model.",
21429
+ name: "get_model_details"
21430
+ },
21431
+ {
21432
+ category: "read",
21433
+ description: "Search the third-party (HuggingFace) model catalogue for models available to add.",
21434
+ name: "search_3rd_party"
21435
+ },
21436
+ {
21437
+ category: "write",
21438
+ description: "Register a new model in the account.",
21439
+ name: "create_model"
21440
+ },
21441
+ {
21442
+ category: "write",
21443
+ description: "Update a model's name, task type or multimodal flag.",
21444
+ name: "update_model"
21445
+ },
21446
+ { category: "delete", description: "Delete a model.", name: "delete_model" },
21447
+ {
21448
+ category: "read",
21449
+ description: "List hardware machines registered to the account.",
21450
+ name: "list_machines"
21451
+ },
21452
+ {
21453
+ category: "read",
21454
+ description: "Get details for a single hardware machine.",
21455
+ name: "get_machine_details"
21456
+ },
21457
+ { category: "read", description: "List API keys in the account.", name: "list_api_keys" },
21458
+ {
21459
+ category: "write",
21460
+ description: "Create a new API key and return its secret value.",
21461
+ name: "create_api_key"
21462
+ },
21463
+ { category: "delete", description: "Delete an API key.", name: "delete_api_key" },
21464
+ { category: "read", description: "List tools in the account.", name: "list_tools" },
21465
+ {
21466
+ category: "read",
21467
+ description: "Get full details for a single tool.",
21468
+ name: "get_tool_details"
21469
+ },
21470
+ { category: "write", description: "Create a new tool.", name: "create_tool" },
21471
+ {
21472
+ category: "write",
21473
+ description: "Update a tool's name, type or configuration.",
21474
+ name: "update_tool"
21475
+ },
21476
+ { category: "delete", description: "Delete a tool.", name: "delete_tool" },
21477
+ {
21478
+ category: "read",
21479
+ description: "List tool services in the account.",
21480
+ name: "list_tool_services"
21481
+ },
21482
+ {
21483
+ category: "read",
21484
+ description: "Get full details for a single tool service.",
21485
+ name: "get_tool_service_details"
21486
+ },
21487
+ { category: "write", description: "Create a new tool service.", name: "create_tool_service" },
21488
+ {
21489
+ category: "write",
21490
+ description: "Update a tool service's name, enabled flag or public exposure.",
21491
+ name: "update_tool_service"
21492
+ },
21493
+ {
21494
+ category: "write",
21495
+ description: "Enable or disable a tool service.",
21496
+ name: "toggle_tool_service"
21497
+ },
21498
+ { category: "delete", description: "Delete a tool service.", name: "delete_tool_service" },
21499
+ {
21500
+ category: "read",
21501
+ description: "Get the tool service currently attached to an endpoint, if any.",
21502
+ name: "get_endpoint_tool_service"
21503
+ },
21504
+ {
21505
+ category: "write",
21506
+ description: "Attach a tool service to an endpoint (replaces any existing attachment).",
21507
+ name: "attach_tool_service_to_endpoint"
21508
+ },
21509
+ {
21510
+ category: "write",
21511
+ description: "Detach the tool service from an endpoint.",
21512
+ name: "detach_tool_service_from_endpoint"
21513
+ },
21514
+ {
21515
+ category: "write",
21516
+ description: "Add a tool to a tool service.",
21517
+ name: "add_tool_to_service"
21518
+ },
21519
+ {
21520
+ category: "write",
21521
+ description: "Remove a tool from a tool service.",
21522
+ name: "remove_tool_from_service"
21523
+ },
21524
+ {
21525
+ category: "read",
21526
+ description: "List inference engines in the account.",
21527
+ name: "list_engines"
21528
+ },
21529
+ { category: "write", description: "Create a new inference engine.", name: "create_engine" },
21530
+ { category: "write", description: "Update an inference engine.", name: "update_engine" },
21531
+ { category: "delete", description: "Delete an inference engine.", name: "delete_engine" }
21532
+ ];
21533
+ const OPERATION_NAMES = [...ACCOUNT_MANAGEMENT_OPERATION_METADATA.map(op => op.name)];
21534
+ const AccountManagementOperationNameSchema = _enum$1(OPERATION_NAMES);
21535
+ const AccountManagementConfigSchema = object$1({
21536
+ allowDelete: boolean$1().default(false),
21537
+ allowRead: boolean$1().default(true),
21538
+ allowWrite: boolean$1().default(false),
21539
+ enabledOperations: array(AccountManagementOperationNameSchema).optional(),
21540
+ type: literal("account-management")
21541
+ });
21542
+
21384
21543
  object$1({
21385
21544
  description: string$2().optional(),
21386
21545
  inputSchema: record(string$2(), unknown()).optional(),
@@ -21413,6 +21572,7 @@ object$1({
21413
21572
  });
21414
21573
 
21415
21574
  _enum$1([
21575
+ "account-management",
21416
21576
  "local-filesystem",
21417
21577
  "local-mysql",
21418
21578
  "mcp-proxy",
@@ -21449,6 +21609,7 @@ const ModelSourceCallConfigSchema = object$1({
21449
21609
  type: literal("model-source-call")
21450
21610
  });
21451
21611
  discriminatedUnion("type", [
21612
+ AccountManagementConfigSchema,
21452
21613
  LocalFilesystemConfigSchema,
21453
21614
  LocalMysqlConfigSchema,
21454
21615
  MCPProxyConfigSchema,
@@ -124207,11 +124368,17 @@ function serializeRequestBody$1(body) {
124207
124368
  payload
124208
124369
  };
124209
124370
  }
124210
- function calculateTokensPerSecond$2({ durationMs, totalTokens }) {
124211
- if (durationMs <= 0) {
124371
+ function calculateTokensPerSecond$1({ completionTokens, durationMs, streamRequested, timeToFirstTokenMs }) {
124372
+ if (completionTokens <= 0 || durationMs <= 0) {
124212
124373
  return 0;
124213
124374
  }
124214
- const tokensPerSecond = totalTokens / (durationMs / 1000);
124375
+ const decodeMs = streamRequested && timeToFirstTokenMs !== null && timeToFirstTokenMs > 0
124376
+ ? Math.max(0, durationMs - timeToFirstTokenMs)
124377
+ : durationMs;
124378
+ if (decodeMs <= 0) {
124379
+ return 0;
124380
+ }
124381
+ const tokensPerSecond = completionTokens / (decodeMs / 1000);
124215
124382
  if (!Number.isFinite(tokensPerSecond) || tokensPerSecond <= 0) {
124216
124383
  return 0;
124217
124384
  }
@@ -124261,10 +124428,7 @@ async function proxyEmbeddingsRoute({ body, conduitConfiguration, endpointId, lo
124261
124428
  responseBytes,
124262
124429
  successful: upstreamResponseOk && !error,
124263
124430
  timeToFirstTokenMs: null,
124264
- tokensPerSecond: calculateTokensPerSecond$2({
124265
- durationMs: latencyMs,
124266
- totalTokens
124267
- }),
124431
+ tokensPerSecond: 0,
124268
124432
  totalTokens
124269
124433
  });
124270
124434
  };
@@ -124407,9 +124571,11 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
124407
124571
  responseBytes,
124408
124572
  successful: upstreamResponseOk && !error,
124409
124573
  timeToFirstTokenMs,
124410
- tokensPerSecond: calculateTokensPerSecond$2({
124574
+ tokensPerSecond: calculateTokensPerSecond$1({
124575
+ completionTokens,
124411
124576
  durationMs: latencyMs,
124412
- totalTokens
124577
+ streamRequested,
124578
+ timeToFirstTokenMs
124413
124579
  }),
124414
124580
  totalTokens
124415
124581
  });
@@ -124857,10 +125023,15 @@ function serializeRequestBody(body) {
124857
125023
  payload
124858
125024
  };
124859
125025
  }
124860
- function calculateTokensPerSecond$1({ durationMs, totalTokens }) {
124861
- if (durationMs <= 0)
125026
+ function calculateTokensPerSecond({ completionTokens, durationMs, streamRequested, timeToFirstTokenMs }) {
125027
+ if (completionTokens <= 0 || durationMs <= 0)
125028
+ return 0;
125029
+ const decodeMs = streamRequested && timeToFirstTokenMs !== null && timeToFirstTokenMs > 0
125030
+ ? Math.max(0, durationMs - timeToFirstTokenMs)
125031
+ : durationMs;
125032
+ if (decodeMs <= 0)
124862
125033
  return 0;
124863
- const tokensPerSecond = totalTokens / (durationMs / 1000);
125034
+ const tokensPerSecond = completionTokens / (decodeMs / 1000);
124864
125035
  if (!Number.isFinite(tokensPerSecond) || tokensPerSecond <= 0)
124865
125036
  return 0;
124866
125037
  return Math.round(tokensPerSecond);
@@ -125038,7 +125209,7 @@ async function proxyAnthropicStreamingRoute({ body, conduitConfiguration, endpoi
125038
125209
  const targetBody = needsTranslation
125039
125210
  ? translateAnthropicRequestToOpenAI(serializedBody).body
125040
125211
  : serializedBody;
125041
- const onMonitoringComplete = ({ durationMs, error, responseBytes, usage }) => {
125212
+ const onMonitoringComplete = ({ durationMs, error, responseBytes, timeToFirstTokenMs, usage }) => {
125042
125213
  const promptTokens = normalizeTokenCount(usage?.inputTokens);
125043
125214
  const completionTokens = normalizeTokenCount(usage?.outputTokens);
125044
125215
  const totalTokens = promptTokens + completionTokens;
@@ -125057,10 +125228,12 @@ async function proxyAnthropicStreamingRoute({ body, conduitConfiguration, endpoi
125057
125228
  requestPath: "/v1/messages",
125058
125229
  responseBytes,
125059
125230
  successful: !error,
125060
- timeToFirstTokenMs: null,
125061
- tokensPerSecond: calculateTokensPerSecond$1({
125231
+ timeToFirstTokenMs,
125232
+ tokensPerSecond: calculateTokensPerSecond({
125233
+ completionTokens,
125062
125234
  durationMs: latencyMs,
125063
- totalTokens
125235
+ streamRequested,
125236
+ timeToFirstTokenMs
125064
125237
  }),
125065
125238
  totalTokens
125066
125239
  });
@@ -125157,6 +125330,7 @@ async function proxyAnthropicStreamingRoute({ body, conduitConfiguration, endpoi
125157
125330
  const passThrough = new PassThrough();
125158
125331
  let responseBytes = 0;
125159
125332
  let completed = false;
125333
+ let firstChunkAt = null;
125160
125334
  const usage = { inputTokens: null, outputTokens: null };
125161
125335
  const upstreamError = response.ok
125162
125336
  ? null
@@ -125169,6 +125343,7 @@ async function proxyAnthropicStreamingRoute({ body, conduitConfiguration, endpoi
125169
125343
  durationMs: Math.max(0, Date.now() - requestStartedAt),
125170
125344
  error,
125171
125345
  responseBytes,
125346
+ timeToFirstTokenMs: firstChunkAt === null ? null : Math.max(0, firstChunkAt - requestStartedAt),
125172
125347
  usage
125173
125348
  });
125174
125349
  }
@@ -125178,6 +125353,8 @@ async function proxyAnthropicStreamingRoute({ body, conduitConfiguration, endpoi
125178
125353
  const chunks = [];
125179
125354
  rawBody.on("data", (chunk) => {
125180
125355
  const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
125356
+ if (firstChunkAt === null)
125357
+ firstChunkAt = Date.now();
125181
125358
  responseBytes += chunkBuffer.length;
125182
125359
  chunks.push(chunkBuffer);
125183
125360
  });
@@ -125260,6 +125437,8 @@ async function proxyAnthropicStreamingRoute({ body, conduitConfiguration, endpoi
125260
125437
  let buffer = "";
125261
125438
  rawBody.on("data", (chunk) => {
125262
125439
  const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
125440
+ if (firstChunkAt === null)
125441
+ firstChunkAt = Date.now();
125263
125442
  responseBytes += chunkBuffer.length;
125264
125443
  buffer += chunkBuffer.toString("utf8");
125265
125444
  const lines = buffer.split("\n");
@@ -125330,6 +125509,8 @@ async function proxyAnthropicStreamingRoute({ body, conduitConfiguration, endpoi
125330
125509
  const chunks = [];
125331
125510
  rawBody.on("data", (chunk) => {
125332
125511
  const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
125512
+ if (firstChunkAt === null)
125513
+ firstChunkAt = Date.now();
125333
125514
  responseBytes += chunkBuffer.length;
125334
125515
  chunks.push(chunkBuffer);
125335
125516
  });
@@ -125480,7 +125661,7 @@ function createHealthHandler() {
125480
125661
  };
125481
125662
  }
125482
125663
 
125483
- async function handleSSERequests({ apiURL, conduitConfiguration, configuration, logger, modelID, onRequest, onRequestEnd, onRequestStart, reportMetrics, signal }) {
125664
+ async function handleSSERequests({ apiURL, configuration, logger, onRequest, onRequestEnd, onRequestStart, signal }) {
125484
125665
  const streamURL = `${apiURL}/conduit/api/v1/source/${configuration.inferenceSourceID}/requests/stream`;
125485
125666
  const maxReconnectDelayMs = 30000;
125486
125667
  let reconnectAttempt = 0;
@@ -125522,14 +125703,11 @@ async function handleSSERequests({ apiURL, conduitConfiguration, configuration,
125522
125703
  handleRequest({
125523
125704
  activeRequests,
125524
125705
  apiURL,
125525
- conduitConfiguration,
125526
125706
  configuration,
125527
125707
  logger,
125528
- modelID,
125529
125708
  onRequest,
125530
125709
  onRequestEnd,
125531
125710
  onRequestStart,
125532
- reportMetrics,
125533
125711
  request: payload,
125534
125712
  signal: effectiveSignal
125535
125713
  }).catch(error => {
@@ -125566,23 +125744,12 @@ async function handleSSERequests({ apiURL, conduitConfiguration, configuration,
125566
125744
  }
125567
125745
  }
125568
125746
  }
125569
- async function handleRequest({ activeRequests, apiURL, conduitConfiguration, configuration, logger, modelID, onRequest, onRequestEnd, onRequestStart, reportMetrics, request, signal }) {
125570
- function reportMetricsSafe(payload) {
125571
- reportMetrics(payload).catch(error => {
125572
- logger.warn("Failed to upload LLM prompt metrics", {
125573
- error: asError(error),
125574
- requestUrl: request.path
125575
- });
125576
- });
125577
- }
125578
- const engineType = conduitConfiguration().engineConfig?.type ?? null;
125579
- const endpointId = request.parameters?.endpointID ?? null;
125747
+ async function handleRequest({ activeRequests, apiURL, configuration, logger, onRequest, onRequestEnd, onRequestStart, request, signal }) {
125580
125748
  const requestStartedAt = Date.now();
125581
- const requestBytes = calculateRequestBytes(request.body ?? null);
125582
125749
  try {
125583
125750
  await onRequestStart?.(request);
125584
125751
  const response = await onRequest({ request, signal });
125585
- const responseMetrics = await streamResponse({
125752
+ await streamResponse({
125586
125753
  apiURL,
125587
125754
  configuration,
125588
125755
  logger,
@@ -125591,30 +125758,6 @@ async function handleRequest({ activeRequests, apiURL, conduitConfiguration, con
125591
125758
  response,
125592
125759
  signal
125593
125760
  });
125594
- const latencyMs = Math.max(0, Date.now() - requestStartedAt);
125595
- const totalTokens = 0;
125596
- const tokensPerSecond = calculateTokensPerSecond({
125597
- durationMs: latencyMs,
125598
- totalTokens
125599
- });
125600
- reportMetricsSafe({
125601
- bytes: requestBytes + responseMetrics.responseBytes,
125602
- completionTokens: 0,
125603
- engine: engineType,
125604
- endpointId,
125605
- latencyMs,
125606
- modelId: modelID,
125607
- promptTokens: 0,
125608
- requestBytes,
125609
- requestId: request.requestID,
125610
- requestMethod: request.method,
125611
- requestPath: request.path,
125612
- responseBytes: responseMetrics.responseBytes,
125613
- successful: responseMetrics.status < 400,
125614
- timeToFirstTokenMs: responseMetrics.timeToFirstTokenMs,
125615
- tokensPerSecond,
125616
- totalTokens
125617
- });
125618
125761
  }
125619
125762
  catch (error) {
125620
125763
  const isCancelled = signal?.aborted;
@@ -125630,8 +125773,6 @@ async function handleRequest({ activeRequests, apiURL, conduitConfiguration, con
125630
125773
  });
125631
125774
  }
125632
125775
  const failureMessage = "Bad gateway\n\nProxying failed";
125633
- const failureBytes = Buffer.byteLength(failureMessage, "utf8");
125634
- const latencyMs = Math.max(0, Date.now() - requestStartedAt);
125635
125776
  if (!isCancelled) {
125636
125777
  const streamHandler = await sendChunkStream({
125637
125778
  apiURL,
@@ -125651,24 +125792,6 @@ async function handleRequest({ activeRequests, apiURL, conduitConfiguration, con
125651
125792
  });
125652
125793
  await streamHandler.end();
125653
125794
  }
125654
- reportMetricsSafe({
125655
- bytes: isCancelled ? requestBytes : requestBytes + failureBytes,
125656
- completionTokens: 0,
125657
- engine: engineType,
125658
- endpointId,
125659
- latencyMs,
125660
- modelId: modelID,
125661
- promptTokens: 0,
125662
- requestBytes,
125663
- requestId: request.requestID,
125664
- requestMethod: request.method,
125665
- requestPath: request.path,
125666
- responseBytes: isCancelled ? 0 : failureBytes,
125667
- successful: false,
125668
- timeToFirstTokenMs: isCancelled ? null : latencyMs,
125669
- tokensPerSecond: 0,
125670
- totalTokens: 0
125671
- });
125672
125795
  }
125673
125796
  finally {
125674
125797
  activeRequests.delete(request.requestID);
@@ -125856,25 +125979,6 @@ async function sendChunkStream({ apiURL, configuration, requestID, logger }) {
125856
125979
  abort
125857
125980
  };
125858
125981
  }
125859
- function calculateRequestBytes(body) {
125860
- if (body === null || body === undefined) {
125861
- return 0;
125862
- }
125863
- if (typeof body === "string") {
125864
- return Buffer.byteLength(body, "utf8");
125865
- }
125866
- return Buffer.byteLength(JSON.stringify(body), "utf8");
125867
- }
125868
- function calculateTokensPerSecond({ durationMs, totalTokens }) {
125869
- if (durationMs <= 0) {
125870
- return 0;
125871
- }
125872
- const tokensPerSecond = totalTokens / (durationMs / 1000);
125873
- if (!Number.isFinite(tokensPerSecond) || tokensPerSecond <= 0) {
125874
- return 0;
125875
- }
125876
- return Math.round(tokensPerSecond);
125877
- }
125878
125982
 
125879
125983
  const ENGINE_FETCH_TIMEOUT_MS = 7_200_000;
125880
125984
  const LOOPBACK_AGENT = new undiciExports.Agent({
@@ -136047,10 +136151,8 @@ async function createApplication({ abortController, apiClient, configuration, lo
136047
136151
  });
136048
136152
  handleSSERequests({
136049
136153
  apiURL: configuration.apiURL,
136050
- conduitConfiguration: () => conduitConfiguration,
136051
136154
  configuration,
136052
136155
  logger,
136053
- modelID: conduitConfiguration.targetModel.id,
136054
136156
  onRequest: async ({ request, signal }) => {
136055
136157
  return proxyRequest({
136056
136158
  configuration,
@@ -136067,7 +136169,6 @@ async function createApplication({ abortController, apiClient, configuration, lo
136067
136169
  conduitStateManager.incrementActiveRequestCount();
136068
136170
  conduitStateReportManager.reportStateChange();
136069
136171
  },
136070
- reportMetrics: apiClient.reportPromptMetrics,
136071
136172
  signal: abortController.signal
136072
136173
  }).catch(error => {
136073
136174
  logger.error("SSE handler failed", {
package/dist/cli.sea.cjs CHANGED
@@ -21396,6 +21396,165 @@ object$1({
21396
21396
  password: RawPasswordSchema
21397
21397
  });
21398
21398
 
21399
+ _enum$1(["delete", "read", "write"]);
21400
+ const ACCOUNT_MANAGEMENT_OPERATION_METADATA = [
21401
+ {
21402
+ category: "read",
21403
+ description: "List inference endpoints in the account.",
21404
+ name: "list_endpoints"
21405
+ },
21406
+ {
21407
+ category: "read",
21408
+ description: "Get full details for a single inference endpoint, including its sources and tool service.",
21409
+ name: "get_endpoint_details"
21410
+ },
21411
+ { category: "write", description: "Create a new inference endpoint.", name: "create_endpoint" },
21412
+ {
21413
+ category: "write",
21414
+ description: "Update an inference endpoint's name, routing method or sources.",
21415
+ name: "update_endpoint"
21416
+ },
21417
+ {
21418
+ category: "write",
21419
+ description: "Enable or disable an inference endpoint.",
21420
+ name: "toggle_endpoint"
21421
+ },
21422
+ { category: "delete", description: "Delete an inference endpoint.", name: "delete_endpoint" },
21423
+ {
21424
+ category: "read",
21425
+ description: "List inference sources in the account.",
21426
+ name: "list_sources"
21427
+ },
21428
+ {
21429
+ category: "read",
21430
+ description: "Get full details for a single inference source.",
21431
+ name: "get_source_details"
21432
+ },
21433
+ { category: "write", description: "Create a new inference source.", name: "create_source" },
21434
+ {
21435
+ category: "write",
21436
+ description: "Update an inference source's name, engine, model or context length.",
21437
+ name: "update_source"
21438
+ },
21439
+ { category: "delete", description: "Delete an inference source.", name: "delete_source" },
21440
+ { category: "read", description: "List models in the account.", name: "list_models" },
21441
+ {
21442
+ category: "read",
21443
+ description: "Get full details for a single model.",
21444
+ name: "get_model_details"
21445
+ },
21446
+ {
21447
+ category: "read",
21448
+ description: "Search the third-party (HuggingFace) model catalogue for models available to add.",
21449
+ name: "search_3rd_party"
21450
+ },
21451
+ {
21452
+ category: "write",
21453
+ description: "Register a new model in the account.",
21454
+ name: "create_model"
21455
+ },
21456
+ {
21457
+ category: "write",
21458
+ description: "Update a model's name, task type or multimodal flag.",
21459
+ name: "update_model"
21460
+ },
21461
+ { category: "delete", description: "Delete a model.", name: "delete_model" },
21462
+ {
21463
+ category: "read",
21464
+ description: "List hardware machines registered to the account.",
21465
+ name: "list_machines"
21466
+ },
21467
+ {
21468
+ category: "read",
21469
+ description: "Get details for a single hardware machine.",
21470
+ name: "get_machine_details"
21471
+ },
21472
+ { category: "read", description: "List API keys in the account.", name: "list_api_keys" },
21473
+ {
21474
+ category: "write",
21475
+ description: "Create a new API key and return its secret value.",
21476
+ name: "create_api_key"
21477
+ },
21478
+ { category: "delete", description: "Delete an API key.", name: "delete_api_key" },
21479
+ { category: "read", description: "List tools in the account.", name: "list_tools" },
21480
+ {
21481
+ category: "read",
21482
+ description: "Get full details for a single tool.",
21483
+ name: "get_tool_details"
21484
+ },
21485
+ { category: "write", description: "Create a new tool.", name: "create_tool" },
21486
+ {
21487
+ category: "write",
21488
+ description: "Update a tool's name, type or configuration.",
21489
+ name: "update_tool"
21490
+ },
21491
+ { category: "delete", description: "Delete a tool.", name: "delete_tool" },
21492
+ {
21493
+ category: "read",
21494
+ description: "List tool services in the account.",
21495
+ name: "list_tool_services"
21496
+ },
21497
+ {
21498
+ category: "read",
21499
+ description: "Get full details for a single tool service.",
21500
+ name: "get_tool_service_details"
21501
+ },
21502
+ { category: "write", description: "Create a new tool service.", name: "create_tool_service" },
21503
+ {
21504
+ category: "write",
21505
+ description: "Update a tool service's name, enabled flag or public exposure.",
21506
+ name: "update_tool_service"
21507
+ },
21508
+ {
21509
+ category: "write",
21510
+ description: "Enable or disable a tool service.",
21511
+ name: "toggle_tool_service"
21512
+ },
21513
+ { category: "delete", description: "Delete a tool service.", name: "delete_tool_service" },
21514
+ {
21515
+ category: "read",
21516
+ description: "Get the tool service currently attached to an endpoint, if any.",
21517
+ name: "get_endpoint_tool_service"
21518
+ },
21519
+ {
21520
+ category: "write",
21521
+ description: "Attach a tool service to an endpoint (replaces any existing attachment).",
21522
+ name: "attach_tool_service_to_endpoint"
21523
+ },
21524
+ {
21525
+ category: "write",
21526
+ description: "Detach the tool service from an endpoint.",
21527
+ name: "detach_tool_service_from_endpoint"
21528
+ },
21529
+ {
21530
+ category: "write",
21531
+ description: "Add a tool to a tool service.",
21532
+ name: "add_tool_to_service"
21533
+ },
21534
+ {
21535
+ category: "write",
21536
+ description: "Remove a tool from a tool service.",
21537
+ name: "remove_tool_from_service"
21538
+ },
21539
+ {
21540
+ category: "read",
21541
+ description: "List inference engines in the account.",
21542
+ name: "list_engines"
21543
+ },
21544
+ { category: "write", description: "Create a new inference engine.", name: "create_engine" },
21545
+ { category: "write", description: "Update an inference engine.", name: "update_engine" },
21546
+ { category: "delete", description: "Delete an inference engine.", name: "delete_engine" }
21547
+ ];
21548
+ const OPERATION_NAMES = [...ACCOUNT_MANAGEMENT_OPERATION_METADATA.map(op => op.name)];
21549
+ const AccountManagementOperationNameSchema = _enum$1(OPERATION_NAMES);
21550
+ const AccountManagementConfigSchema = object$1({
21551
+ allowDelete: boolean$1().default(false),
21552
+ allowRead: boolean$1().default(true),
21553
+ allowWrite: boolean$1().default(false),
21554
+ enabledOperations: array(AccountManagementOperationNameSchema).optional(),
21555
+ type: literal("account-management")
21556
+ });
21557
+
21399
21558
  object$1({
21400
21559
  description: string$2().optional(),
21401
21560
  inputSchema: record(string$2(), unknown()).optional(),
@@ -21428,6 +21587,7 @@ object$1({
21428
21587
  });
21429
21588
 
21430
21589
  _enum$1([
21590
+ "account-management",
21431
21591
  "local-filesystem",
21432
21592
  "local-mysql",
21433
21593
  "mcp-proxy",
@@ -21464,6 +21624,7 @@ const ModelSourceCallConfigSchema = object$1({
21464
21624
  type: literal("model-source-call")
21465
21625
  });
21466
21626
  discriminatedUnion("type", [
21627
+ AccountManagementConfigSchema,
21467
21628
  LocalFilesystemConfigSchema,
21468
21629
  LocalMysqlConfigSchema,
21469
21630
  MCPProxyConfigSchema,
@@ -124222,11 +124383,17 @@ function serializeRequestBody$1(body) {
124222
124383
  payload
124223
124384
  };
124224
124385
  }
124225
- function calculateTokensPerSecond$2({ durationMs, totalTokens }) {
124226
- if (durationMs <= 0) {
124386
+ function calculateTokensPerSecond$1({ completionTokens, durationMs, streamRequested, timeToFirstTokenMs }) {
124387
+ if (completionTokens <= 0 || durationMs <= 0) {
124227
124388
  return 0;
124228
124389
  }
124229
- const tokensPerSecond = totalTokens / (durationMs / 1000);
124390
+ const decodeMs = streamRequested && timeToFirstTokenMs !== null && timeToFirstTokenMs > 0
124391
+ ? Math.max(0, durationMs - timeToFirstTokenMs)
124392
+ : durationMs;
124393
+ if (decodeMs <= 0) {
124394
+ return 0;
124395
+ }
124396
+ const tokensPerSecond = completionTokens / (decodeMs / 1000);
124230
124397
  if (!Number.isFinite(tokensPerSecond) || tokensPerSecond <= 0) {
124231
124398
  return 0;
124232
124399
  }
@@ -124276,10 +124443,7 @@ async function proxyEmbeddingsRoute({ body, conduitConfiguration, endpointId, lo
124276
124443
  responseBytes,
124277
124444
  successful: upstreamResponseOk && !error,
124278
124445
  timeToFirstTokenMs: null,
124279
- tokensPerSecond: calculateTokensPerSecond$2({
124280
- durationMs: latencyMs,
124281
- totalTokens
124282
- }),
124446
+ tokensPerSecond: 0,
124283
124447
  totalTokens
124284
124448
  });
124285
124449
  };
@@ -124422,9 +124586,11 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
124422
124586
  responseBytes,
124423
124587
  successful: upstreamResponseOk && !error,
124424
124588
  timeToFirstTokenMs,
124425
- tokensPerSecond: calculateTokensPerSecond$2({
124589
+ tokensPerSecond: calculateTokensPerSecond$1({
124590
+ completionTokens,
124426
124591
  durationMs: latencyMs,
124427
- totalTokens
124592
+ streamRequested,
124593
+ timeToFirstTokenMs
124428
124594
  }),
124429
124595
  totalTokens
124430
124596
  });
@@ -124872,10 +125038,15 @@ function serializeRequestBody(body) {
124872
125038
  payload
124873
125039
  };
124874
125040
  }
124875
- function calculateTokensPerSecond$1({ durationMs, totalTokens }) {
124876
- if (durationMs <= 0)
125041
+ function calculateTokensPerSecond({ completionTokens, durationMs, streamRequested, timeToFirstTokenMs }) {
125042
+ if (completionTokens <= 0 || durationMs <= 0)
125043
+ return 0;
125044
+ const decodeMs = streamRequested && timeToFirstTokenMs !== null && timeToFirstTokenMs > 0
125045
+ ? Math.max(0, durationMs - timeToFirstTokenMs)
125046
+ : durationMs;
125047
+ if (decodeMs <= 0)
124877
125048
  return 0;
124878
- const tokensPerSecond = totalTokens / (durationMs / 1000);
125049
+ const tokensPerSecond = completionTokens / (decodeMs / 1000);
124879
125050
  if (!Number.isFinite(tokensPerSecond) || tokensPerSecond <= 0)
124880
125051
  return 0;
124881
125052
  return Math.round(tokensPerSecond);
@@ -125053,7 +125224,7 @@ async function proxyAnthropicStreamingRoute({ body, conduitConfiguration, endpoi
125053
125224
  const targetBody = needsTranslation
125054
125225
  ? translateAnthropicRequestToOpenAI(serializedBody).body
125055
125226
  : serializedBody;
125056
- const onMonitoringComplete = ({ durationMs, error, responseBytes, usage }) => {
125227
+ const onMonitoringComplete = ({ durationMs, error, responseBytes, timeToFirstTokenMs, usage }) => {
125057
125228
  const promptTokens = normalizeTokenCount(usage?.inputTokens);
125058
125229
  const completionTokens = normalizeTokenCount(usage?.outputTokens);
125059
125230
  const totalTokens = promptTokens + completionTokens;
@@ -125072,10 +125243,12 @@ async function proxyAnthropicStreamingRoute({ body, conduitConfiguration, endpoi
125072
125243
  requestPath: "/v1/messages",
125073
125244
  responseBytes,
125074
125245
  successful: !error,
125075
- timeToFirstTokenMs: null,
125076
- tokensPerSecond: calculateTokensPerSecond$1({
125246
+ timeToFirstTokenMs,
125247
+ tokensPerSecond: calculateTokensPerSecond({
125248
+ completionTokens,
125077
125249
  durationMs: latencyMs,
125078
- totalTokens
125250
+ streamRequested,
125251
+ timeToFirstTokenMs
125079
125252
  }),
125080
125253
  totalTokens
125081
125254
  });
@@ -125172,6 +125345,7 @@ async function proxyAnthropicStreamingRoute({ body, conduitConfiguration, endpoi
125172
125345
  const passThrough = new require$$0$8.PassThrough();
125173
125346
  let responseBytes = 0;
125174
125347
  let completed = false;
125348
+ let firstChunkAt = null;
125175
125349
  const usage = { inputTokens: null, outputTokens: null };
125176
125350
  const upstreamError = response.ok
125177
125351
  ? null
@@ -125184,6 +125358,7 @@ async function proxyAnthropicStreamingRoute({ body, conduitConfiguration, endpoi
125184
125358
  durationMs: Math.max(0, Date.now() - requestStartedAt),
125185
125359
  error,
125186
125360
  responseBytes,
125361
+ timeToFirstTokenMs: firstChunkAt === null ? null : Math.max(0, firstChunkAt - requestStartedAt),
125187
125362
  usage
125188
125363
  });
125189
125364
  }
@@ -125193,6 +125368,8 @@ async function proxyAnthropicStreamingRoute({ body, conduitConfiguration, endpoi
125193
125368
  const chunks = [];
125194
125369
  rawBody.on("data", (chunk) => {
125195
125370
  const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
125371
+ if (firstChunkAt === null)
125372
+ firstChunkAt = Date.now();
125196
125373
  responseBytes += chunkBuffer.length;
125197
125374
  chunks.push(chunkBuffer);
125198
125375
  });
@@ -125275,6 +125452,8 @@ async function proxyAnthropicStreamingRoute({ body, conduitConfiguration, endpoi
125275
125452
  let buffer = "";
125276
125453
  rawBody.on("data", (chunk) => {
125277
125454
  const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
125455
+ if (firstChunkAt === null)
125456
+ firstChunkAt = Date.now();
125278
125457
  responseBytes += chunkBuffer.length;
125279
125458
  buffer += chunkBuffer.toString("utf8");
125280
125459
  const lines = buffer.split("\n");
@@ -125345,6 +125524,8 @@ async function proxyAnthropicStreamingRoute({ body, conduitConfiguration, endpoi
125345
125524
  const chunks = [];
125346
125525
  rawBody.on("data", (chunk) => {
125347
125526
  const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
125527
+ if (firstChunkAt === null)
125528
+ firstChunkAt = Date.now();
125348
125529
  responseBytes += chunkBuffer.length;
125349
125530
  chunks.push(chunkBuffer);
125350
125531
  });
@@ -125495,7 +125676,7 @@ function createHealthHandler() {
125495
125676
  };
125496
125677
  }
125497
125678
 
125498
- async function handleSSERequests({ apiURL, conduitConfiguration, configuration, logger, modelID, onRequest, onRequestEnd, onRequestStart, reportMetrics, signal }) {
125679
+ async function handleSSERequests({ apiURL, configuration, logger, onRequest, onRequestEnd, onRequestStart, signal }) {
125499
125680
  const streamURL = `${apiURL}/conduit/api/v1/source/${configuration.inferenceSourceID}/requests/stream`;
125500
125681
  const maxReconnectDelayMs = 30000;
125501
125682
  let reconnectAttempt = 0;
@@ -125537,14 +125718,11 @@ async function handleSSERequests({ apiURL, conduitConfiguration, configuration,
125537
125718
  handleRequest({
125538
125719
  activeRequests,
125539
125720
  apiURL,
125540
- conduitConfiguration,
125541
125721
  configuration,
125542
125722
  logger,
125543
- modelID,
125544
125723
  onRequest,
125545
125724
  onRequestEnd,
125546
125725
  onRequestStart,
125547
- reportMetrics,
125548
125726
  request: payload,
125549
125727
  signal: effectiveSignal
125550
125728
  }).catch(error => {
@@ -125581,23 +125759,12 @@ async function handleSSERequests({ apiURL, conduitConfiguration, configuration,
125581
125759
  }
125582
125760
  }
125583
125761
  }
125584
- async function handleRequest({ activeRequests, apiURL, conduitConfiguration, configuration, logger, modelID, onRequest, onRequestEnd, onRequestStart, reportMetrics, request, signal }) {
125585
- function reportMetricsSafe(payload) {
125586
- reportMetrics(payload).catch(error => {
125587
- logger.warn("Failed to upload LLM prompt metrics", {
125588
- error: asError(error),
125589
- requestUrl: request.path
125590
- });
125591
- });
125592
- }
125593
- const engineType = conduitConfiguration().engineConfig?.type ?? null;
125594
- const endpointId = request.parameters?.endpointID ?? null;
125762
+ async function handleRequest({ activeRequests, apiURL, configuration, logger, onRequest, onRequestEnd, onRequestStart, request, signal }) {
125595
125763
  const requestStartedAt = Date.now();
125596
- const requestBytes = calculateRequestBytes(request.body ?? null);
125597
125764
  try {
125598
125765
  await onRequestStart?.(request);
125599
125766
  const response = await onRequest({ request, signal });
125600
- const responseMetrics = await streamResponse({
125767
+ await streamResponse({
125601
125768
  apiURL,
125602
125769
  configuration,
125603
125770
  logger,
@@ -125606,30 +125773,6 @@ async function handleRequest({ activeRequests, apiURL, conduitConfiguration, con
125606
125773
  response,
125607
125774
  signal
125608
125775
  });
125609
- const latencyMs = Math.max(0, Date.now() - requestStartedAt);
125610
- const totalTokens = 0;
125611
- const tokensPerSecond = calculateTokensPerSecond({
125612
- durationMs: latencyMs,
125613
- totalTokens
125614
- });
125615
- reportMetricsSafe({
125616
- bytes: requestBytes + responseMetrics.responseBytes,
125617
- completionTokens: 0,
125618
- engine: engineType,
125619
- endpointId,
125620
- latencyMs,
125621
- modelId: modelID,
125622
- promptTokens: 0,
125623
- requestBytes,
125624
- requestId: request.requestID,
125625
- requestMethod: request.method,
125626
- requestPath: request.path,
125627
- responseBytes: responseMetrics.responseBytes,
125628
- successful: responseMetrics.status < 400,
125629
- timeToFirstTokenMs: responseMetrics.timeToFirstTokenMs,
125630
- tokensPerSecond,
125631
- totalTokens
125632
- });
125633
125776
  }
125634
125777
  catch (error) {
125635
125778
  const isCancelled = signal?.aborted;
@@ -125645,8 +125788,6 @@ async function handleRequest({ activeRequests, apiURL, conduitConfiguration, con
125645
125788
  });
125646
125789
  }
125647
125790
  const failureMessage = "Bad gateway\n\nProxying failed";
125648
- const failureBytes = Buffer.byteLength(failureMessage, "utf8");
125649
- const latencyMs = Math.max(0, Date.now() - requestStartedAt);
125650
125791
  if (!isCancelled) {
125651
125792
  const streamHandler = await sendChunkStream({
125652
125793
  apiURL,
@@ -125666,24 +125807,6 @@ async function handleRequest({ activeRequests, apiURL, conduitConfiguration, con
125666
125807
  });
125667
125808
  await streamHandler.end();
125668
125809
  }
125669
- reportMetricsSafe({
125670
- bytes: isCancelled ? requestBytes : requestBytes + failureBytes,
125671
- completionTokens: 0,
125672
- engine: engineType,
125673
- endpointId,
125674
- latencyMs,
125675
- modelId: modelID,
125676
- promptTokens: 0,
125677
- requestBytes,
125678
- requestId: request.requestID,
125679
- requestMethod: request.method,
125680
- requestPath: request.path,
125681
- responseBytes: isCancelled ? 0 : failureBytes,
125682
- successful: false,
125683
- timeToFirstTokenMs: isCancelled ? null : latencyMs,
125684
- tokensPerSecond: 0,
125685
- totalTokens: 0
125686
- });
125687
125810
  }
125688
125811
  finally {
125689
125812
  activeRequests.delete(request.requestID);
@@ -125871,25 +125994,6 @@ async function sendChunkStream({ apiURL, configuration, requestID, logger }) {
125871
125994
  abort
125872
125995
  };
125873
125996
  }
125874
- function calculateRequestBytes(body) {
125875
- if (body === null || body === undefined) {
125876
- return 0;
125877
- }
125878
- if (typeof body === "string") {
125879
- return Buffer.byteLength(body, "utf8");
125880
- }
125881
- return Buffer.byteLength(JSON.stringify(body), "utf8");
125882
- }
125883
- function calculateTokensPerSecond({ durationMs, totalTokens }) {
125884
- if (durationMs <= 0) {
125885
- return 0;
125886
- }
125887
- const tokensPerSecond = totalTokens / (durationMs / 1000);
125888
- if (!Number.isFinite(tokensPerSecond) || tokensPerSecond <= 0) {
125889
- return 0;
125890
- }
125891
- return Math.round(tokensPerSecond);
125892
- }
125893
125997
 
125894
125998
  const ENGINE_FETCH_TIMEOUT_MS = 7_200_000;
125895
125999
  const LOOPBACK_AGENT = new undiciExports.Agent({
@@ -156277,10 +156381,8 @@ async function createApplication({ abortController, apiClient, configuration, lo
156277
156381
  });
156278
156382
  handleSSERequests({
156279
156383
  apiURL: configuration.apiURL,
156280
- conduitConfiguration: () => conduitConfiguration,
156281
156384
  configuration,
156282
156385
  logger,
156283
- modelID: conduitConfiguration.targetModel.id,
156284
156386
  onRequest: async ({ request, signal }) => {
156285
156387
  return proxyRequest({
156286
156388
  configuration,
@@ -156297,7 +156399,6 @@ async function createApplication({ abortController, apiClient, configuration, lo
156297
156399
  conduitStateManager.incrementActiveRequestCount();
156298
156400
  conduitStateReportManager.reportStateChange();
156299
156401
  },
156300
- reportMetrics: apiClient.reportPromptMetrics,
156301
156402
  signal: abortController.signal
156302
156403
  }).catch(error => {
156303
156404
  logger.error("SSE handler failed", {
@@ -1,18 +1,15 @@
1
- import { InferenceAgentConfiguration, InferenceAgentLLMMetricsPayload, type ULID, type APIResponse, type ServerToClientAPIRequest } from "@infersec/definitions";
1
+ import { type APIResponse, type ServerToClientAPIRequest } from "@infersec/definitions";
2
2
  import { Logger } from "@infersec/logger";
3
3
  import { Configuration } from "../configuration.js";
4
- export declare function handleSSERequests({ apiURL, conduitConfiguration, configuration, logger, modelID, onRequest, onRequestEnd, onRequestStart, reportMetrics, signal }: {
4
+ export declare function handleSSERequests({ apiURL, configuration, logger, onRequest, onRequestEnd, onRequestStart, signal }: {
5
5
  apiURL: string;
6
- conduitConfiguration: () => InferenceAgentConfiguration;
7
6
  configuration: Configuration;
8
7
  logger: Logger;
9
- modelID: ULID;
10
8
  onRequest: ({ request, signal }: {
11
9
  request: ServerToClientAPIRequest;
12
10
  signal?: AbortSignal;
13
11
  }) => Promise<APIResponse>;
14
12
  onRequestEnd?: (request: ServerToClientAPIRequest) => Promise<void> | void;
15
13
  onRequestStart?: (request: ServerToClientAPIRequest) => Promise<void> | void;
16
- reportMetrics: (payload: InferenceAgentLLMMetricsPayload) => Promise<void>;
17
14
  signal?: AbortSignal;
18
15
  }): Promise<void>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@infersec/conduit",
3
3
  "description": "End user conduit agent for connecting local LLMs to the cloud.",
4
- "version": "1.98.1",
4
+ "version": "1.99.0",
5
5
  "bin": {
6
6
  "infersec-conduit": "./dist/cli.js"
7
7
  },