@infersec/conduit 1.8.0 → 1.8.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/cli.js CHANGED
@@ -6,7 +6,7 @@ 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-CVgqDYvF.js';
9
+ import { a as asError, s as startInferenceAgent } from './start-LWLy96m_.js';
10
10
  import 'argon2';
11
11
  import 'node:child_process';
12
12
  import 'node:stream';
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ 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-CVgqDYvF.js';
8
+ import { s as startInferenceAgent, a as asError } from './start-LWLy96m_.js';
9
9
  import 'argon2';
10
10
  import 'node:child_process';
11
11
  import 'node:stream';
@@ -30,5 +30,7 @@ export declare class ModelManager extends EventEmitter<ModelManagerEvents> {
30
30
  onDownloadProgress?: (update: ModelDownloadProgressUpdate) => void;
31
31
  }): Promise<void>;
32
32
  start(): Promise<void>;
33
+ private isEngineReady;
34
+ private waitForEngineReady;
33
35
  }
34
36
  export {};
@@ -1,11 +1,12 @@
1
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, configuration, logger, onRequest, onRequestEnd, onRequestStart }: {
4
+ export declare function handleSSERequests({ apiURL, configuration, logger, onRequest, onRequestEnd, onRequestStart, signal }: {
5
5
  apiURL: string;
6
6
  configuration: Configuration;
7
7
  logger: Logger;
8
8
  onRequest: (request: ServerToClientAPIRequest) => Promise<APIResponse>;
9
9
  onRequestEnd?: (request: ServerToClientAPIRequest) => Promise<void> | void;
10
10
  onRequestStart?: (request: ServerToClientAPIRequest) => Promise<void> | void;
11
+ signal?: AbortSignal;
11
12
  }): Promise<void>;
@@ -105091,42 +105091,112 @@ class ModelManager extends EventEmitter {
105091
105091
  this.logger.info("Started LLM engine", {
105092
105092
  agentEngineType: this.engine
105093
105093
  });
105094
+ try {
105095
+ await this.waitForEngineReady();
105096
+ }
105097
+ catch (error) {
105098
+ const err = error instanceof Error ? error : new Error(String(error));
105099
+ this.emit("engineError", err);
105100
+ throw err;
105101
+ }
105094
105102
  this.emit("engineReady");
105095
105103
  }
105104
+ async isEngineReady() {
105105
+ switch (this.engine) {
105106
+ case "llama.cpp":
105107
+ case "vllm": {
105108
+ try {
105109
+ const response = await this.fetchOpenAI("/v1/models", {
105110
+ method: "GET",
105111
+ signal: AbortSignal.timeout(5000)
105112
+ });
105113
+ return response.ok;
105114
+ }
105115
+ catch (_error) {
105116
+ return false;
105117
+ }
105118
+ }
105119
+ default:
105120
+ return true;
105121
+ }
105122
+ }
105123
+ async waitForEngineReady() {
105124
+ const maxWaitMs = 5 * 60 * 1000;
105125
+ const pollIntervalMs = 2000;
105126
+ const start = Date.now();
105127
+ while (Date.now() - start < maxWaitMs) {
105128
+ const ready = await this.isEngineReady();
105129
+ if (ready) {
105130
+ return;
105131
+ }
105132
+ await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
105133
+ }
105134
+ throw new Error("LLM engine failed readiness checks within timeout");
105135
+ }
105136
+ }
105137
+
105138
+ function sleep(ms) {
105139
+ return new Promise(resolve => {
105140
+ setTimeout(() => resolve(), ms);
105141
+ });
105096
105142
  }
105097
105143
 
105098
- async function handleSSERequests({ apiURL, configuration, logger, onRequest, onRequestEnd, onRequestStart }) {
105144
+ async function handleSSERequests({ apiURL, configuration, logger, onRequest, onRequestEnd, onRequestStart, signal }) {
105099
105145
  const streamURL = `${apiURL}/conduit/api/v1/source/${configuration.inferenceSourceID}/requests/stream`;
105100
- await connectSSE(streamURL, {
105101
- headers: {
105102
- "x-api-key": configuration.apiKey
105103
- },
105104
- onError: (error) => {
105105
- logger.error("SSE connection error", {
105106
- error
105146
+ const maxReconnectDelayMs = 30000;
105147
+ let reconnectAttempt = 0;
105148
+ while (!signal?.aborted) {
105149
+ const connectionStartedAt = Date.now();
105150
+ try {
105151
+ await connectSSE(streamURL, {
105152
+ headers: {
105153
+ "x-api-key": configuration.apiKey
105154
+ },
105155
+ onError: (error) => {
105156
+ logger.error("SSE connection error", {
105157
+ error
105158
+ });
105159
+ },
105160
+ onMessage: (message) => {
105161
+ if (message.event !== "request") {
105162
+ return;
105163
+ }
105164
+ const payload = ServerToClientAPIRequestSchema.parse(JSON.parse(message.data));
105165
+ handleRequest({
105166
+ apiURL,
105167
+ configuration,
105168
+ logger,
105169
+ onRequest,
105170
+ onRequestEnd,
105171
+ onRequestStart,
105172
+ request: payload
105173
+ }).catch(error => {
105174
+ logger.error("SSE request handler failed", {
105175
+ error: asError(error),
105176
+ requestMethod: payload.requestID
105177
+ });
105178
+ });
105179
+ },
105180
+ signal
105107
105181
  });
105108
- },
105109
- onMessage: (message) => {
105110
- if (message.event !== "request") {
105182
+ }
105183
+ catch (error) {
105184
+ if (signal?.aborted) {
105111
105185
  return;
105112
105186
  }
105113
- const payload = ServerToClientAPIRequestSchema.parse(JSON.parse(message.data));
105114
- handleRequest({
105115
- apiURL,
105116
- configuration,
105117
- logger,
105118
- onRequestEnd,
105119
- onRequestStart,
105120
- onRequest,
105121
- request: payload
105122
- }).catch(error => {
105123
- logger.error("SSE request handler failed", {
105124
- error: asError(error),
105125
- requestMethod: payload.requestID
105126
- });
105187
+ logger.error("SSE connection failed", {
105188
+ error: asError(error)
105127
105189
  });
105128
105190
  }
105129
- });
105191
+ if (signal?.aborted) {
105192
+ return;
105193
+ }
105194
+ const connectionDurationMs = Date.now() - connectionStartedAt;
105195
+ reconnectAttempt = connectionDurationMs > 10000 ? 0 : reconnectAttempt + 1;
105196
+ const reconnectDelayMs = Math.min(maxReconnectDelayMs, Math.max(1000, 1000 * 2 ** Math.min(6, reconnectAttempt)));
105197
+ logger.warn("SSE disconnected, retrying");
105198
+ await sleep(reconnectDelayMs);
105199
+ }
105130
105200
  }
105131
105201
  async function handleRequest({ apiURL, configuration, logger, onRequest, onRequestEnd, onRequestStart, request }) {
105132
105202
  try {
@@ -114784,7 +114854,6 @@ async function createApplication({ abortController, apiClient, configuration, lo
114784
114854
  });
114785
114855
  const modelFileName = getConduitModelFileName(conduitConfiguration);
114786
114856
  const modelName = getConduitModelName(conduitConfiguration);
114787
- const idleReason = "Awaiting requests";
114788
114857
  const startup = Date.now();
114789
114858
  // Initialise model manager
114790
114859
  const modelManager = new ModelManager({
@@ -114795,28 +114864,6 @@ async function createApplication({ abortController, apiClient, configuration, lo
114795
114864
  parallelism: conduitConfiguration.parallelism ?? null,
114796
114865
  root: configuration.rootDirectory
114797
114866
  });
114798
- modelManager.on("engineError", err => {
114799
- logger.error("LLM engine error", {
114800
- error: err
114801
- });
114802
- conduitStateManager.setState({
114803
- error: err.message,
114804
- state: "error"
114805
- });
114806
- abortController.abort(err);
114807
- });
114808
- modelManager.on("engineTerminated", () => {
114809
- conduitStateManager.setState({
114810
- state: "offline"
114811
- });
114812
- abortController.abort();
114813
- });
114814
- modelManager.on("engineReady", () => {
114815
- conduitStateManager.setState({
114816
- reason: idleReason,
114817
- state: "idle"
114818
- });
114819
- });
114820
114867
  conduitStateManager.setState({
114821
114868
  modelFileName,
114822
114869
  modelName,
@@ -114931,40 +114978,54 @@ async function createApplication({ abortController, apiClient, configuration, lo
114931
114978
  });
114932
114979
  }, CONDUIT_STATE_INTERVAL_MS);
114933
114980
  let activeRequests = 0;
114934
- const setIdleState = () => {
114935
- conduitStateManager.setState({
114936
- reason: idleReason,
114937
- state: "idle"
114938
- });
114939
- };
114940
114981
  const setOnlineState = () => {
114941
114982
  conduitStateManager.setState({
114942
114983
  modelName,
114943
114984
  state: "online"
114944
114985
  });
114945
114986
  };
114987
+ modelManager.on("engineError", err => {
114988
+ logger.error("LLM engine error", {
114989
+ error: err
114990
+ });
114991
+ conduitStateManager.setState({
114992
+ error: err.message,
114993
+ state: "error"
114994
+ });
114995
+ abortController.abort(err);
114996
+ });
114997
+ modelManager.on("engineTerminated", () => {
114998
+ conduitStateManager.setState({
114999
+ state: "offline"
115000
+ });
115001
+ abortController.abort();
115002
+ });
115003
+ modelManager.on("engineReady", () => {
115004
+ setOnlineState();
115005
+ });
114946
115006
  handleSSERequests({
114947
115007
  apiURL: configuration.apiURL,
114948
115008
  configuration,
114949
115009
  logger,
114950
- onRequestEnd: () => {
114951
- activeRequests = Math.max(0, activeRequests - 1);
114952
- if (activeRequests === 0) {
114953
- setIdleState();
114954
- }
114955
- },
114956
115010
  onRequest: async (request) => {
114957
115011
  return proxyRequest({
114958
115012
  configuration,
114959
115013
  request
114960
115014
  });
114961
115015
  },
115016
+ onRequestEnd: () => {
115017
+ activeRequests = Math.max(0, activeRequests - 1);
115018
+ if (activeRequests === 0) {
115019
+ setOnlineState();
115020
+ }
115021
+ },
114962
115022
  onRequestStart: () => {
114963
115023
  activeRequests += 1;
114964
115024
  if (activeRequests === 1) {
114965
115025
  setOnlineState();
114966
115026
  }
114967
- }
115027
+ },
115028
+ signal: abortController.signal
114968
115029
  }).catch(error => {
114969
115030
  logger.error("SSE handler failed", {
114970
115031
  error: asError(error)
@@ -115004,12 +115065,6 @@ function getConfiguration({ overrides } = {}) {
115004
115065
  };
115005
115066
  }
115006
115067
 
115007
- function sleep(ms) {
115008
- return new Promise(resolve => {
115009
- setTimeout(() => resolve(), ms);
115010
- });
115011
- }
115012
-
115013
115068
  async function startInferenceAgent({ configurationOverrides }) {
115014
115069
  const abortController = new AbortController();
115015
115070
  const configuration = getConfiguration({ overrides: configurationOverrides });
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.8.0",
4
+ "version": "1.8.2",
5
5
  "bin": {
6
6
  "infersec-conduit": "./dist/cli.js"
7
7
  },