@infersec/conduit 1.26.1 → 1.26.3

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,12 +1,14 @@
1
1
  import { InferenceAgentConfiguration, InferenceAgentLLMMetricsPayload, InferenceAgentMachineReportPayload, ULID, type ConduitState } from "@infersec/definitions";
2
+ import { Logger } from "@infersec/logger";
2
3
  export interface APIClient {
3
4
  getConduitConfiguration: () => Promise<InferenceAgentConfiguration>;
4
5
  reportConduitState: (state: ConduitState) => Promise<void>;
5
6
  reportMachineMetadata: (payload: InferenceAgentMachineReportPayload) => Promise<void>;
6
7
  reportPromptMetrics: (payload: InferenceAgentLLMMetricsPayload) => Promise<void>;
7
8
  }
8
- export declare function createAPIClient({ apiKey, apiURL, inferenceSourceID }: {
9
+ export declare function createAPIClient({ apiKey, apiURL, inferenceSourceID, logger }: {
9
10
  apiKey: string;
10
11
  apiURL: string;
11
12
  inferenceSourceID: ULID;
13
+ logger: Logger;
12
14
  }): APIClient;
package/dist/cli.js CHANGED
@@ -6,10 +6,11 @@ const __dirname = __pathDirname(__filename);
6
6
 
7
7
  import { parseArgs } from 'node:util';
8
8
  import 'node:crypto';
9
- import { a as asError, s as startInferenceAgent } from './start-PzV0cQI5.js';
9
+ import { a as asError, s as startInferenceAgent } from './start-D4bpW8Ix.js';
10
10
  import 'argon2';
11
11
  import 'node:child_process';
12
12
  import 'node:stream';
13
+ import 'node:dns';
13
14
  import 'os';
14
15
  import 'fs';
15
16
  import 'util';
@@ -34,7 +35,6 @@ import 'node:console';
34
35
  import 'node:fs/promises';
35
36
  import 'node:path';
36
37
  import 'node:timers';
37
- import 'node:dns';
38
38
  import 'path';
39
39
  import 'tty';
40
40
  import 'net';
package/dist/index.js CHANGED
@@ -5,10 +5,11 @@ const __filename = __fileURLToPath(import.meta.url);
5
5
  const __dirname = __pathDirname(__filename);
6
6
 
7
7
  import 'node:crypto';
8
- import { s as startInferenceAgent, a as asError } from './start-PzV0cQI5.js';
8
+ import { s as startInferenceAgent, a as asError } from './start-D4bpW8Ix.js';
9
9
  import 'argon2';
10
10
  import 'node:child_process';
11
11
  import 'node:stream';
12
+ import 'node:dns';
12
13
  import 'os';
13
14
  import 'fs';
14
15
  import 'util';
@@ -34,7 +35,6 @@ import 'node:console';
34
35
  import 'node:fs/promises';
35
36
  import 'node:path';
36
37
  import 'node:timers';
37
- import 'node:dns';
38
38
  import 'path';
39
39
  import 'tty';
40
40
  import 'net';
@@ -4,6 +4,7 @@ import { dirname as __pathDirname } from 'node:path';
4
4
  const __filename = __fileURLToPath(import.meta.url);
5
5
  const __dirname = __pathDirname(__filename);
6
6
 
7
+ import require$$1$6, { setDefaultResultOrder } from 'node:dns';
7
8
  import require$$0$5 from 'os';
8
9
  import require$$1$2, { realpathSync as realpathSync$1, readlinkSync, readdirSync, readdir as readdir$1, lstatSync } from 'fs';
9
10
  import require$$2$2 from 'util';
@@ -33,7 +34,6 @@ import require$$1$5 from 'node:console';
33
34
  import require$$0$b, { mkdir, readFile, writeFile, stat, unlink, rename, realpath, readlink, readdir, lstat } from 'node:fs/promises';
34
35
  import path$1, { join, dirname, win32, posix } from 'node:path';
35
36
  import require$$2$5 from 'node:timers';
36
- import require$$1$6 from 'node:dns';
37
37
  import require$$0$c from 'path';
38
38
  import require$$0$d from 'tty';
39
39
  import require$$4$3 from 'net';
@@ -98039,8 +98039,136 @@ function implementAPIReference({ api, logger, mount, reference }) {
98039
98039
  }
98040
98040
  }
98041
98041
 
98042
- function createAPIClient({ apiKey, apiURL, inferenceSourceID }) {
98042
+ function sleep(ms) {
98043
+ return new Promise(resolve => {
98044
+ setTimeout(() => resolve(), ms);
98045
+ });
98046
+ }
98047
+
98048
+ async function fetchWithRetry(input, init, { baseDelayMs = 250, maxAttempts = 4, maxDelayMs = 2000, signal, timeoutMs = 10000 } = {}) {
98049
+ let lastError = null;
98050
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
98051
+ if (signal?.aborted) {
98052
+ throw asError(signal.reason);
98053
+ }
98054
+ const attemptController = new AbortController();
98055
+ const timeout = setTimeout(() => {
98056
+ attemptController.abort(new Error(`Request timeout (${timeoutMs}ms)`));
98057
+ }, timeoutMs);
98058
+ const composedSignal = composeAbortSignals({
98059
+ primary: signal,
98060
+ secondary: attemptController.signal
98061
+ });
98062
+ try {
98063
+ return await fetch(input, {
98064
+ ...init,
98065
+ signal: composedSignal
98066
+ });
98067
+ }
98068
+ catch (error) {
98069
+ const parsedError = asError(error);
98070
+ lastError = parsedError;
98071
+ if (!isRetryableNetworkError(parsedError) || attempt >= maxAttempts) {
98072
+ throw parsedError;
98073
+ }
98074
+ const delayMs = Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1));
98075
+ const jitterMs = Math.floor(Math.random() * 100);
98076
+ await sleep(delayMs + jitterMs);
98077
+ }
98078
+ finally {
98079
+ clearTimeout(timeout);
98080
+ }
98081
+ }
98082
+ throw lastError ?? new Error("Request failed");
98083
+ }
98084
+ function getNetworkErrorAttributes(error) {
98085
+ const parsedError = asError(error);
98086
+ const attributes = {};
98087
+ let cursor = parsedError;
98088
+ let depth = 0;
98089
+ while (cursor && depth < 3) {
98090
+ const networkCursor = cursor;
98091
+ if (depth === 0) {
98092
+ attributes.errorMessage = cursor.message;
98093
+ attributes.errorName = cursor.name;
98094
+ }
98095
+ const prefix = depth === 0 ? "error" : `cause${String(depth)}`;
98096
+ if (typeof networkCursor.code === "string") {
98097
+ attributes[`${prefix}Code`] = networkCursor.code;
98098
+ }
98099
+ if (typeof networkCursor.errno === "string" || typeof networkCursor.errno === "number") {
98100
+ attributes[`${prefix}Errno`] = networkCursor.errno;
98101
+ }
98102
+ if (typeof networkCursor.syscall === "string") {
98103
+ attributes[`${prefix}Syscall`] = networkCursor.syscall;
98104
+ }
98105
+ if (typeof networkCursor.address === "string") {
98106
+ attributes[`${prefix}Address`] = networkCursor.address;
98107
+ }
98108
+ if (typeof networkCursor.port === "number") {
98109
+ attributes[`${prefix}Port`] = networkCursor.port;
98110
+ }
98111
+ cursor = cursor.cause instanceof Error ? cursor.cause : undefined;
98112
+ depth += 1;
98113
+ }
98114
+ return attributes;
98115
+ }
98116
+ function composeAbortSignals({ primary, secondary }) {
98117
+ if (!primary) {
98118
+ return secondary;
98119
+ }
98120
+ if (primary.aborted) {
98121
+ return primary;
98122
+ }
98123
+ const composedController = new AbortController();
98124
+ const abort = (reason) => {
98125
+ if (!composedController.signal.aborted) {
98126
+ composedController.abort(reason);
98127
+ }
98128
+ };
98129
+ const primaryListener = () => {
98130
+ abort(primary.reason);
98131
+ };
98132
+ const secondaryListener = () => {
98133
+ abort(secondary.reason);
98134
+ };
98135
+ primary.addEventListener("abort", primaryListener, { once: true });
98136
+ secondary.addEventListener("abort", secondaryListener, { once: true });
98137
+ return composedController.signal;
98138
+ }
98139
+ function isRetryableNetworkError(error) {
98140
+ if (error.name === "AbortError") {
98141
+ return true;
98142
+ }
98143
+ const message = error.message.toLowerCase();
98144
+ if (message.includes("fetch failed") || message.includes("timeout")) {
98145
+ return true;
98146
+ }
98147
+ const errorCode = error.code;
98148
+ return (errorCode === "ECONNRESET" ||
98149
+ errorCode === "ECONNREFUSED" ||
98150
+ errorCode === "ETIMEDOUT" ||
98151
+ errorCode === "EHOSTUNREACH" ||
98152
+ errorCode === "ENETUNREACH" ||
98153
+ errorCode === "ENOTFOUND" ||
98154
+ errorCode === "EAI_AGAIN");
98155
+ }
98156
+
98157
+ function createAPIClient({ apiKey, apiURL, inferenceSourceID, logger }) {
98158
+ async function retryingFetch(input, init) {
98159
+ try {
98160
+ return await fetchWithRetry(input, init, {
98161
+ maxAttempts: 4,
98162
+ timeoutMs: 10000
98163
+ });
98164
+ }
98165
+ catch (error) {
98166
+ logger.warn("API request failed after retries", getNetworkErrorAttributes(error));
98167
+ throw error;
98168
+ }
98169
+ }
98043
98170
  const fetchWithAPIKey = createFetchWithHeaders({
98171
+ fetchFn: retryingFetch,
98044
98172
  headers: {
98045
98173
  "x-api-key": apiKey
98046
98174
  }
@@ -108454,12 +108582,6 @@ class ModelManager extends EventEmitter {
108454
108582
  }
108455
108583
  }
108456
108584
 
108457
- function sleep(ms) {
108458
- return new Promise(resolve => {
108459
- setTimeout(() => resolve(), ms);
108460
- });
108461
- }
108462
-
108463
108585
  async function handleSSERequests({ apiURL, configuration, logger, modelID, onRequest, onRequestEnd, onRequestStart, reportMetrics, signal }) {
108464
108586
  const streamURL = `${apiURL}/conduit/api/v1/source/${configuration.inferenceSourceID}/requests/stream`;
108465
108587
  const maxReconnectDelayMs = 30000;
@@ -108509,6 +108631,7 @@ async function handleSSERequests({ apiURL, configuration, logger, modelID, onReq
108509
108631
  const isTerminated = isTerminatedError(error);
108510
108632
  if (!isTerminated) {
108511
108633
  logger.error("SSE connection failed", {
108634
+ ...getNetworkErrorAttributes(error),
108512
108635
  error: asError(error)
108513
108636
  });
108514
108637
  }
@@ -108736,7 +108859,7 @@ async function sendChunkStream({ apiURL, configuration, requestID, logger }) {
108736
108859
  for (let attempt = 1; attempt <= maxFlushAttempts; attempt += 1) {
108737
108860
  try {
108738
108861
  activeAbortController = new AbortController();
108739
- const response = await fetch(streamURL, {
108862
+ const response = await fetchWithRetry(streamURL, {
108740
108863
  body: body.toString(),
108741
108864
  headers: {
108742
108865
  "content-type": "application/json",
@@ -108744,6 +108867,9 @@ async function sendChunkStream({ apiURL, configuration, requestID, logger }) {
108744
108867
  },
108745
108868
  method: "POST",
108746
108869
  signal: activeAbortController.signal
108870
+ }, {
108871
+ maxAttempts: 2,
108872
+ timeoutMs: 15000
108747
108873
  });
108748
108874
  if (!response.ok) {
108749
108875
  throw new Error(`Chunk stream flush failed with status ${response.status}`);
@@ -108759,6 +108885,7 @@ async function sendChunkStream({ apiURL, configuration, requestID, logger }) {
108759
108885
  throw asError(error);
108760
108886
  }
108761
108887
  logger.warn("Failed to send chunk batch", {
108888
+ ...getNetworkErrorAttributes(error),
108762
108889
  error: asError(error)
108763
108890
  });
108764
108891
  await sleep(100 * attempt);
@@ -108911,6 +109038,7 @@ class ConduitStateReportManager {
108911
109038
  }
108912
109039
  catch (error) {
108913
109040
  this.logger.error("Conduit state update failed", {
109041
+ ...getNetworkErrorAttributes(error),
108914
109042
  error: asError(error)
108915
109043
  });
108916
109044
  }
@@ -119191,6 +119319,7 @@ function getConfiguration({ overrides } = {}) {
119191
119319
  }
119192
119320
 
119193
119321
  async function startInferenceAgent({ configurationOverrides }) {
119322
+ setDefaultResultOrder("ipv4first");
119194
119323
  const abortController = new AbortController();
119195
119324
  const configuration = getConfiguration({ overrides: configurationOverrides });
119196
119325
  const logger = createLogger({
@@ -119202,7 +119331,8 @@ async function startInferenceAgent({ configurationOverrides }) {
119202
119331
  const apiClient = createAPIClient({
119203
119332
  apiKey: configuration.apiKey,
119204
119333
  apiURL: configuration.apiURL,
119205
- inferenceSourceID: configuration.inferenceSourceID
119334
+ inferenceSourceID: configuration.inferenceSourceID,
119335
+ logger
119206
119336
  });
119207
119337
  logger.info("Starting web server");
119208
119338
  const { app, shutdown } = await createApplication({
@@ -0,0 +1,8 @@
1
+ export declare function fetchWithRetry(input: URL | string, init: RequestInit, { baseDelayMs, maxAttempts, maxDelayMs, signal, timeoutMs }?: {
2
+ baseDelayMs?: number;
3
+ maxAttempts?: number;
4
+ maxDelayMs?: number;
5
+ signal?: AbortSignal;
6
+ timeoutMs?: number;
7
+ }): Promise<Response>;
8
+ export declare function getNetworkErrorAttributes(error: unknown): Record<string, unknown>;
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.26.1",
4
+ "version": "1.26.3",
5
5
  "bin": {
6
6
  "infersec-conduit": "./dist/cli.js"
7
7
  },