@infersec/conduit 1.46.0 → 1.46.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -112782,6 +112782,7 @@ class ModelManager extends EventEmitter {
112782
112782
  contextLength;
112783
112783
  logger;
112784
112784
  engineProcess = null;
112785
+ healthPollInterval = null;
112785
112786
  lifecycleState = "stopped";
112786
112787
  stopRequested = false;
112787
112788
  modelsDirectory;
@@ -112919,6 +112920,9 @@ class ModelManager extends EventEmitter {
112919
112920
  }
112920
112921
  this.lifecycleState = "errored";
112921
112922
  this.emit("engineError", err);
112923
+ if (this.engineProcess) {
112924
+ this.startHealthPoll();
112925
+ }
112922
112926
  throw err;
112923
112927
  }
112924
112928
  this.lifecycleState = "running";
@@ -112930,16 +112934,18 @@ class ModelManager extends EventEmitter {
112930
112934
  message: "Cannot stop LLM engine: already stopping"
112931
112935
  });
112932
112936
  }
112933
- if (this.lifecycleState !== "running" && this.lifecycleState !== "starting") {
112937
+ if (this.lifecycleState !== "running" &&
112938
+ this.lifecycleState !== "starting" &&
112939
+ this.lifecycleState !== "errored") {
112934
112940
  throw new UnknownError({
112935
112941
  message: `Cannot stop LLM engine: Invalid state: ${this.lifecycleState}`
112936
112942
  });
112937
112943
  }
112944
+ this.clearHealthPoll();
112938
112945
  const processManager = this.engineProcess;
112939
112946
  if (!processManager) {
112940
- throw new UnknownError({
112941
- message: "Cannot stop LLM engine: engine process not running"
112942
- });
112947
+ this.lifecycleState = "stopped";
112948
+ return;
112943
112949
  }
112944
112950
  this.lifecycleState = "stopping";
112945
112951
  this.stopRequested = true;
@@ -112949,32 +112955,63 @@ class ModelManager extends EventEmitter {
112949
112955
  return this.lifecycleState === "stopped";
112950
112956
  }
112951
112957
  get canStop() {
112952
- return this.lifecycleState === "running" || this.lifecycleState === "starting";
112958
+ return (this.lifecycleState === "running" ||
112959
+ this.lifecycleState === "starting" ||
112960
+ this.lifecycleState === "errored");
112953
112961
  }
112954
112962
  get state() {
112955
112963
  return this.lifecycleState;
112956
112964
  }
112957
- async isEngineReady() {
112965
+ async checkEngineReadiness() {
112958
112966
  switch (this.engine) {
112959
- case "llama.cpp":
112967
+ case "llama.cpp": {
112968
+ return this.checkLlamacppReadiness();
112969
+ }
112960
112970
  case "vllm": {
112961
- try {
112962
- const response = await this.fetchOpenAI("/v1/models", {
112963
- method: "GET",
112964
- signal: AbortSignal.timeout(5000)
112965
- });
112966
- return response.ok;
112967
- }
112968
- catch (_error) {
112969
- return false;
112970
- }
112971
+ return this.checkVLLMReadiness();
112971
112972
  }
112972
112973
  default:
112973
- return true;
112974
+ return "ready";
112975
+ }
112976
+ }
112977
+ async checkLlamacppReadiness() {
112978
+ try {
112979
+ const response = await undiciExports.fetch(joinURL(`http://localhost:${this.enginePort}`, "/health"), {
112980
+ method: "GET",
112981
+ signal: AbortSignal.timeout(5000)
112982
+ });
112983
+ if (response.status === 503) {
112984
+ return "loading";
112985
+ }
112986
+ if (response.ok) {
112987
+ return "ready";
112988
+ }
112989
+ return "loading";
112990
+ }
112991
+ catch (_error) {
112992
+ return "unreachable";
112993
+ }
112994
+ }
112995
+ async checkVLLMReadiness() {
112996
+ try {
112997
+ const response = await undiciExports.fetch(joinURL(`http://localhost:${this.enginePort}`, "/v1/models"), {
112998
+ method: "GET",
112999
+ signal: AbortSignal.timeout(5000)
113000
+ });
113001
+ if (response.status === 503) {
113002
+ return "loading";
113003
+ }
113004
+ if (response.ok) {
113005
+ return "ready";
113006
+ }
113007
+ return "loading";
113008
+ }
113009
+ catch (_error) {
113010
+ return "unreachable";
112974
113011
  }
112975
113012
  }
112976
113013
  async waitForEngineReady() {
112977
- const maxWaitMs = 5 * 60 * 1000;
113014
+ const maxWaitMs = 15 * 60 * 1000;
112978
113015
  const pollIntervalMs = 2000;
112979
113016
  const start = Date.now();
112980
113017
  while (Date.now() - start < maxWaitMs) {
@@ -112984,14 +113021,41 @@ class ModelManager extends EventEmitter {
112984
113021
  if (!this.engineProcess) {
112985
113022
  throw new Error("LLM engine process exited before readiness checks completed");
112986
113023
  }
112987
- const ready = await this.isEngineReady();
112988
- if (ready) {
113024
+ const readiness = await this.checkEngineReadiness();
113025
+ if (readiness === "ready") {
112989
113026
  return;
112990
113027
  }
112991
113028
  await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
112992
113029
  }
112993
113030
  throw new Error("LLM engine failed readiness checks within timeout");
112994
113031
  }
113032
+ clearHealthPoll() {
113033
+ if (this.healthPollInterval) {
113034
+ clearInterval(this.healthPollInterval);
113035
+ this.healthPollInterval = null;
113036
+ }
113037
+ }
113038
+ startHealthPoll() {
113039
+ this.clearHealthPoll();
113040
+ this.logger.info("Starting background health poll for errored engine");
113041
+ this.healthPollInterval = setInterval(() => {
113042
+ if (!this.engineProcess) {
113043
+ this.clearHealthPoll();
113044
+ return;
113045
+ }
113046
+ this.checkEngineReadiness()
113047
+ .then(readiness => {
113048
+ if (readiness === "ready") {
113049
+ this.clearHealthPoll();
113050
+ this.lifecycleState = "running";
113051
+ this.emit("engineReady");
113052
+ }
113053
+ })
113054
+ .catch(() => {
113055
+ // keep polling
113056
+ });
113057
+ }, 15_000);
113058
+ }
112995
113059
  bindEngineProcessEvents(processManager) {
112996
113060
  let hasTerminated = false;
112997
113061
  processManager.on("stderr", line => {
@@ -113005,6 +113069,7 @@ class ModelManager extends EventEmitter {
113005
113069
  return;
113006
113070
  }
113007
113071
  hasTerminated = true;
113072
+ this.clearHealthPoll();
113008
113073
  if (this.stopRequested) {
113009
113074
  this.engineProcess = null;
113010
113075
  this.lifecycleState = "stopped";
@@ -113026,6 +113091,7 @@ class ModelManager extends EventEmitter {
113026
113091
  return;
113027
113092
  }
113028
113093
  hasTerminated = true;
113094
+ this.clearHealthPoll();
113029
113095
  this.engineProcess = null;
113030
113096
  if (this.stopRequested) {
113031
113097
  this.lifecycleState = "stopped";
@@ -18,6 +18,7 @@ export declare class ModelManager extends EventEmitter<ModelManagerEvents> {
18
18
  readonly contextLength: number | null;
19
19
  protected readonly logger: Logger;
20
20
  private engineProcess;
21
+ private healthPollInterval;
21
22
  private lifecycleState;
22
23
  private stopRequested;
23
24
  protected readonly modelsDirectory: string;
@@ -39,8 +40,12 @@ export declare class ModelManager extends EventEmitter<ModelManagerEvents> {
39
40
  get canStart(): boolean;
40
41
  get canStop(): boolean;
41
42
  get state(): EngineLifecycleState;
42
- private isEngineReady;
43
+ private checkEngineReadiness;
44
+ private checkLlamacppReadiness;
45
+ private checkVLLMReadiness;
43
46
  private waitForEngineReady;
47
+ private clearHealthPoll;
48
+ private startHealthPoll;
44
49
  private bindEngineProcessEvents;
45
50
  private startEngineProcess;
46
51
  }
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.46.0",
4
+ "version": "1.46.1",
5
5
  "bin": {
6
6
  "infersec-conduit": "./dist/cli.js"
7
7
  },