@infersec/conduit 1.113.0 → 1.114.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 +341 -73
- package/dist/cli.sea.cjs +341 -73
- package/dist/commands/engineOptions.d.ts +1 -0
- package/dist/modelManagement/ModelManager.d.ts +5 -0
- package/dist/reporting/engineExecutionReporter.d.ts +2 -1
- package/dist/utils/engineMetrics.d.ts +3 -2
- package/dist/utils/openai.d.ts +8 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -19886,6 +19886,7 @@ object$5({
|
|
|
19886
19886
|
});
|
|
19887
19887
|
|
|
19888
19888
|
const LLMEngineSchema = _enum$1([
|
|
19889
|
+
"custom",
|
|
19889
19890
|
"exllamav3",
|
|
19890
19891
|
"llama.cpp",
|
|
19891
19892
|
"mlx-lm",
|
|
@@ -19894,8 +19895,25 @@ const LLMEngineSchema = _enum$1([
|
|
|
19894
19895
|
"vllm"
|
|
19895
19896
|
]);
|
|
19896
19897
|
const EngineConfigSchema = object$5({
|
|
19898
|
+
baseUrl: string$2().url().nullable().default(null),
|
|
19897
19899
|
extraArgs: array$1(string$2()),
|
|
19898
19900
|
type: LLMEngineSchema
|
|
19901
|
+
})
|
|
19902
|
+
.superRefine((config, ctx) => {
|
|
19903
|
+
if (config.type === "custom" && config.baseUrl === null) {
|
|
19904
|
+
ctx.addIssue({
|
|
19905
|
+
code: "custom",
|
|
19906
|
+
message: "Engine type 'custom' requires a base URL",
|
|
19907
|
+
path: ["baseUrl"]
|
|
19908
|
+
});
|
|
19909
|
+
}
|
|
19910
|
+
if (config.type !== "custom" && config.baseUrl !== null) {
|
|
19911
|
+
ctx.addIssue({
|
|
19912
|
+
code: "custom",
|
|
19913
|
+
message: "Base URL is only valid for engine type 'custom'",
|
|
19914
|
+
path: ["baseUrl"]
|
|
19915
|
+
});
|
|
19916
|
+
}
|
|
19899
19917
|
});
|
|
19900
19918
|
const LLMModelFormatSchema = _enum$1([
|
|
19901
19919
|
// VLLM / SGLang / TensorRT-LLM
|
|
@@ -19954,14 +19972,18 @@ const LLMModelSchema = object$5({
|
|
|
19954
19972
|
id: string$2().min(1),
|
|
19955
19973
|
multimodalEnabled: boolean$1(),
|
|
19956
19974
|
source: discriminatedUnion("type", [
|
|
19975
|
+
// Custom engines: no local model record, serving is external
|
|
19957
19976
|
object$5({
|
|
19958
|
-
|
|
19959
|
-
type: literal("storage")
|
|
19977
|
+
type: literal("external")
|
|
19960
19978
|
}),
|
|
19961
19979
|
object$5({
|
|
19962
19980
|
modelSecret: string$2().min(1).nullable(),
|
|
19963
19981
|
slug: string$2().min(1),
|
|
19964
19982
|
type: literal("huggingface")
|
|
19983
|
+
}),
|
|
19984
|
+
object$5({
|
|
19985
|
+
irid: IRIDSchema,
|
|
19986
|
+
type: literal("storage")
|
|
19965
19987
|
})
|
|
19966
19988
|
]),
|
|
19967
19989
|
taskType: LLMModelTaskTypeSchema,
|
|
@@ -21105,7 +21127,8 @@ const CreateModelResponseSchema = object$5({
|
|
|
21105
21127
|
const CreateSourceBodySchema = object$5({
|
|
21106
21128
|
contextLength: number$1().int().positive().max(1048576).optional(),
|
|
21107
21129
|
engineId: ULIDSchema,
|
|
21108
|
-
|
|
21130
|
+
// Optional for custom engines, which serve externally managed models
|
|
21131
|
+
modelID: ULIDSchema.nullable().optional(),
|
|
21109
21132
|
name: ResourceNameSchema,
|
|
21110
21133
|
quantizationLabel: string$2().min(1).max(128).optional()
|
|
21111
21134
|
});
|
|
@@ -21154,7 +21177,7 @@ const SourceDetailResponseSchema = object$5({
|
|
|
21154
21177
|
const UpdateSourceBodySchema = object$5({
|
|
21155
21178
|
contextLength: number$1().int().positive().nullable().optional(),
|
|
21156
21179
|
engineId: ULIDSchema.nullable().optional(),
|
|
21157
|
-
modelID: ULIDSchema.optional(),
|
|
21180
|
+
modelID: ULIDSchema.nullable().optional(),
|
|
21158
21181
|
name: ResourceNameSchema.optional(),
|
|
21159
21182
|
quantizationLabel: string$2().min(1).max(128).nullable().optional()
|
|
21160
21183
|
});
|
|
@@ -21278,6 +21301,7 @@ const CreateEndpointResponseSchema = object$5({
|
|
|
21278
21301
|
id: ULIDSchema
|
|
21279
21302
|
});
|
|
21280
21303
|
const EngineOutputSchema = object$5({
|
|
21304
|
+
baseUrl: string$2().nullable(),
|
|
21281
21305
|
created: string$2(),
|
|
21282
21306
|
extraArgs: array$1(string$2()),
|
|
21283
21307
|
id: ULIDSchema,
|
|
@@ -21286,11 +21310,29 @@ const EngineOutputSchema = object$5({
|
|
|
21286
21310
|
updated: string$2()
|
|
21287
21311
|
});
|
|
21288
21312
|
const CreateEngineBodySchema = object$5({
|
|
21313
|
+
baseUrl: string$2().url().nullable().optional(),
|
|
21289
21314
|
extraArgs: array$1(string$2()).optional(),
|
|
21290
21315
|
name: ResourceNameSchema,
|
|
21291
21316
|
type: LLMEngineSchema
|
|
21317
|
+
})
|
|
21318
|
+
.superRefine((body, ctx) => {
|
|
21319
|
+
if (body.type === "custom" && !body.baseUrl) {
|
|
21320
|
+
ctx.addIssue({
|
|
21321
|
+
code: "custom",
|
|
21322
|
+
message: "Engine type 'custom' requires a base URL",
|
|
21323
|
+
path: ["baseUrl"]
|
|
21324
|
+
});
|
|
21325
|
+
}
|
|
21326
|
+
if (body.type !== "custom" && body.baseUrl) {
|
|
21327
|
+
ctx.addIssue({
|
|
21328
|
+
code: "custom",
|
|
21329
|
+
message: "Base URL is only valid for engine type 'custom'",
|
|
21330
|
+
path: ["baseUrl"]
|
|
21331
|
+
});
|
|
21332
|
+
}
|
|
21292
21333
|
});
|
|
21293
21334
|
const UpdateEngineBodySchema = object$5({
|
|
21335
|
+
baseUrl: string$2().url().nullable().optional(),
|
|
21294
21336
|
extraArgs: array$1(string$2()).optional(),
|
|
21295
21337
|
name: ResourceNameSchema.optional(),
|
|
21296
21338
|
type: LLMEngineSchema.optional()
|
|
@@ -22096,6 +22138,11 @@ const RecommendedModelSchema = object$5({
|
|
|
22096
22138
|
const recommendedModels = RecommendedModelSchema.array().parse(modelsData);
|
|
22097
22139
|
|
|
22098
22140
|
const ENGINE_API_COMPATIBILITY = {
|
|
22141
|
+
custom: {
|
|
22142
|
+
nativeAnthropicMessages: false,
|
|
22143
|
+
supportsEmbeddings: true,
|
|
22144
|
+
supportsVision: true
|
|
22145
|
+
},
|
|
22099
22146
|
exllamav3: {
|
|
22100
22147
|
nativeAnthropicMessages: false,
|
|
22101
22148
|
supportsEmbeddings: false,
|
|
@@ -111745,6 +111792,27 @@ function registerEndpointCommands({ program }) {
|
|
|
111745
111792
|
}
|
|
111746
111793
|
|
|
111747
111794
|
const ENGINE_TYPES = LLMEngineSchema.options;
|
|
111795
|
+
function isValidURL(value) {
|
|
111796
|
+
try {
|
|
111797
|
+
const url = new URL(value);
|
|
111798
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
111799
|
+
}
|
|
111800
|
+
catch (_error) {
|
|
111801
|
+
return false;
|
|
111802
|
+
}
|
|
111803
|
+
}
|
|
111804
|
+
function validateEngineBaseURL({ baseUrl, type }) {
|
|
111805
|
+
if (type === "custom" && !baseUrl) {
|
|
111806
|
+
throw new Error("Engine type 'custom' requires --base-url");
|
|
111807
|
+
}
|
|
111808
|
+
if (baseUrl !== undefined && type !== "custom") {
|
|
111809
|
+
throw new Error("--base-url is only valid for engine type 'custom'");
|
|
111810
|
+
}
|
|
111811
|
+
if (baseUrl !== undefined && baseUrl !== "" && !isValidURL(baseUrl)) {
|
|
111812
|
+
throw new Error(`Invalid --base-url value: ${baseUrl}`);
|
|
111813
|
+
}
|
|
111814
|
+
return baseUrl === "" ? null : (baseUrl ?? null);
|
|
111815
|
+
}
|
|
111748
111816
|
function buildEngineCreateBody(options) {
|
|
111749
111817
|
if (!options.name) {
|
|
111750
111818
|
throw new Error("--name is required");
|
|
@@ -111755,7 +111823,12 @@ function buildEngineCreateBody(options) {
|
|
|
111755
111823
|
if (!ENGINE_TYPES.includes(options.type)) {
|
|
111756
111824
|
throw new Error(`Invalid engine type: ${options.type} (expected one of: ${ENGINE_TYPES.join(", ")})`);
|
|
111757
111825
|
}
|
|
111826
|
+
const baseUrl = validateEngineBaseURL({
|
|
111827
|
+
baseUrl: options.baseUrl,
|
|
111828
|
+
type: options.type
|
|
111829
|
+
});
|
|
111758
111830
|
return {
|
|
111831
|
+
baseUrl,
|
|
111759
111832
|
extraArgs: options.arg ?? [],
|
|
111760
111833
|
name: options.name,
|
|
111761
111834
|
type: options.type
|
|
@@ -111773,6 +111846,24 @@ function buildEngineUpdateBody(options) {
|
|
|
111773
111846
|
}
|
|
111774
111847
|
if (options.arg !== undefined)
|
|
111775
111848
|
body.extraArgs = options.arg;
|
|
111849
|
+
if (options.baseUrl !== undefined) {
|
|
111850
|
+
const targetType = body.type;
|
|
111851
|
+
if (targetType !== undefined) {
|
|
111852
|
+
body.baseUrl = validateEngineBaseURL({
|
|
111853
|
+
baseUrl: options.baseUrl,
|
|
111854
|
+
type: targetType
|
|
111855
|
+
});
|
|
111856
|
+
}
|
|
111857
|
+
else if (options.baseUrl === "") {
|
|
111858
|
+
body.baseUrl = null;
|
|
111859
|
+
}
|
|
111860
|
+
else if (!isValidURL(options.baseUrl)) {
|
|
111861
|
+
throw new Error(`Invalid --base-url value: ${options.baseUrl}`);
|
|
111862
|
+
}
|
|
111863
|
+
else {
|
|
111864
|
+
body.baseUrl = options.baseUrl;
|
|
111865
|
+
}
|
|
111866
|
+
}
|
|
111776
111867
|
return body;
|
|
111777
111868
|
}
|
|
111778
111869
|
|
|
@@ -111786,10 +111877,11 @@ function registerEngineCommands({ program }) {
|
|
|
111786
111877
|
.description("Create or update an inference engine resource")
|
|
111787
111878
|
.option("--api-url <url>", "API base URL (required, no environment variable fallback)")
|
|
111788
111879
|
.option("--arg <flag>", 'Raw engine CLI flag, repeatable (eg --arg "--flash-attn on")', collect, [])
|
|
111880
|
+
.option("--base-url <url>", "Base URL of an external OpenAI-compatible server (engine type 'custom' only)")
|
|
111789
111881
|
.option("--id <ulid>", "Target an existing engine by ID (requires --update)")
|
|
111790
111882
|
.option("--key <value>", "API key (required, no environment variable fallback)")
|
|
111791
111883
|
.option("--name <name>", "Engine name (matched by --update)")
|
|
111792
|
-
.option("--type <type>", "Engine type: llama.cpp|vllm|sglang|tensorrt-llm|mlx-lm|exllamav3")
|
|
111884
|
+
.option("--type <type>", "Engine type: llama.cpp|vllm|sglang|tensorrt-llm|mlx-lm|exllamav3|custom")
|
|
111793
111885
|
.option("--update", "Update an existing engine matched by name (or --id) instead of erroring")
|
|
111794
111886
|
.action(async (options) => {
|
|
111795
111887
|
const { apiURL, apiKey } = resolveManagementConnection(options);
|
|
@@ -117789,6 +117881,9 @@ async function getChatTemplateEngineArgs({ engine, model, targetDirectory }) {
|
|
|
117789
117881
|
return [];
|
|
117790
117882
|
const flag = FLAG_BASED_ENGINE_ARGS[engine];
|
|
117791
117883
|
if (!flag) {
|
|
117884
|
+
if (engine === "custom") {
|
|
117885
|
+
console.warn("[chatTemplate] Custom engines manage their own serving; ignoring chat template override");
|
|
117886
|
+
}
|
|
117792
117887
|
if (engine === "tensorrt-llm") {
|
|
117793
117888
|
console.warn("[chatTemplate] TensorRT-LLM does not support chat template overrides; ignoring");
|
|
117794
117889
|
}
|
|
@@ -124686,7 +124781,7 @@ function matchesQuantizationVariant({ filePath, variant }) {
|
|
|
124686
124781
|
return segments.slice(0, -1).some(segment => matcher.test(segment));
|
|
124687
124782
|
}
|
|
124688
124783
|
async function findQuantizedModelTarget({ model, path }) {
|
|
124689
|
-
if (model.source.type
|
|
124784
|
+
if (model.source.type !== "huggingface") {
|
|
124690
124785
|
throw new Error("Model storage not supported yet");
|
|
124691
124786
|
}
|
|
124692
124787
|
if (model.format !== "gguf") {
|
|
@@ -125601,7 +125696,11 @@ function sanitizeSegment(value) {
|
|
|
125601
125696
|
.replace(new RegExp(`${SEPARATOR}{2,}`, "g"), SEPARATOR);
|
|
125602
125697
|
}
|
|
125603
125698
|
function createModelStorageKey(model) {
|
|
125604
|
-
const identifier = model.source.type === "huggingface"
|
|
125699
|
+
const identifier = model.source.type === "huggingface"
|
|
125700
|
+
? model.source.slug
|
|
125701
|
+
: model.source.type === "storage"
|
|
125702
|
+
? model.source.irid
|
|
125703
|
+
: model.id;
|
|
125605
125704
|
return `${model.source.type}${SEPARATOR}${sanitizeSegment(identifier)}`;
|
|
125606
125705
|
}
|
|
125607
125706
|
|
|
@@ -125677,6 +125776,7 @@ class ModelManager extends EventEmitter {
|
|
|
125677
125776
|
uniqueName;
|
|
125678
125777
|
contextLength;
|
|
125679
125778
|
logger;
|
|
125779
|
+
discoveredModelNames = [];
|
|
125680
125780
|
engineProcess = null;
|
|
125681
125781
|
healthPollInterval = null;
|
|
125682
125782
|
lastEngineError = null;
|
|
@@ -125709,6 +125809,7 @@ class ModelManager extends EventEmitter {
|
|
|
125709
125809
|
}
|
|
125710
125810
|
async fetchOpenAI(path, opts) {
|
|
125711
125811
|
switch (this.engine) {
|
|
125812
|
+
case "custom":
|
|
125712
125813
|
case "exllamav3":
|
|
125713
125814
|
case "llama.cpp":
|
|
125714
125815
|
case "mlx-lm":
|
|
@@ -125716,6 +125817,9 @@ class ModelManager extends EventEmitter {
|
|
|
125716
125817
|
case "tensorrt-llm":
|
|
125717
125818
|
case "vllm": {
|
|
125718
125819
|
this.logger.debug(`Fetching from engine: ${path}`);
|
|
125820
|
+
const baseURL = this.engine === "custom"
|
|
125821
|
+
? this.requireCustomBaseURL()
|
|
125822
|
+
: `http://localhost:${this.enginePort}`;
|
|
125719
125823
|
const callerSignal = opts?.signal;
|
|
125720
125824
|
const controller = new AbortController();
|
|
125721
125825
|
const timeout = setTimeout(() => {
|
|
@@ -125726,7 +125830,7 @@ class ModelManager extends EventEmitter {
|
|
|
125726
125830
|
: controller.signal;
|
|
125727
125831
|
try {
|
|
125728
125832
|
const fetchStartedAt = Date.now();
|
|
125729
|
-
const response = await undiciExports.fetch(joinURL(
|
|
125833
|
+
const response = await undiciExports.fetch(joinURL(baseURL, path), {
|
|
125730
125834
|
...opts,
|
|
125731
125835
|
dispatcher: ENGINE_AGENT,
|
|
125732
125836
|
headers: {
|
|
@@ -125761,6 +125865,11 @@ class ModelManager extends EventEmitter {
|
|
|
125761
125865
|
modelID: this.model.id
|
|
125762
125866
|
});
|
|
125763
125867
|
switch (this.engine) {
|
|
125868
|
+
case "custom":
|
|
125869
|
+
if (this.model.chatTemplate) {
|
|
125870
|
+
this.logger.warn("Chat template overrides are ignored for custom engines: the remote server manages its own serving");
|
|
125871
|
+
}
|
|
125872
|
+
break;
|
|
125764
125873
|
case "exllamav3":
|
|
125765
125874
|
case "llama.cpp":
|
|
125766
125875
|
case "mlx-lm":
|
|
@@ -125816,7 +125925,9 @@ class ModelManager extends EventEmitter {
|
|
|
125816
125925
|
});
|
|
125817
125926
|
try {
|
|
125818
125927
|
this.engineProcess = await this.startEngineProcess();
|
|
125819
|
-
|
|
125928
|
+
if (this.engineProcess) {
|
|
125929
|
+
this.bindEngineProcessEvents(this.engineProcess);
|
|
125930
|
+
}
|
|
125820
125931
|
this.logger.info("Started LLM engine", {
|
|
125821
125932
|
agentEngineType: this.engine
|
|
125822
125933
|
});
|
|
@@ -125835,7 +125946,7 @@ class ModelManager extends EventEmitter {
|
|
|
125835
125946
|
if (!alreadyEmitted) {
|
|
125836
125947
|
this.emit("engineError", err);
|
|
125837
125948
|
}
|
|
125838
|
-
if (this.engineProcess) {
|
|
125949
|
+
if (this.engineProcess || this.engine === "custom") {
|
|
125839
125950
|
this.startHealthPoll();
|
|
125840
125951
|
}
|
|
125841
125952
|
throw err;
|
|
@@ -125843,6 +125954,9 @@ class ModelManager extends EventEmitter {
|
|
|
125843
125954
|
this.lifecycleState = "running";
|
|
125844
125955
|
this.reachedRunningState = true;
|
|
125845
125956
|
this.emit("engineReady");
|
|
125957
|
+
if (this.engine === "custom") {
|
|
125958
|
+
this.startHealthPoll();
|
|
125959
|
+
}
|
|
125846
125960
|
}
|
|
125847
125961
|
async stop() {
|
|
125848
125962
|
if (this.lifecycleState === "stopping") {
|
|
@@ -125863,6 +125977,8 @@ class ModelManager extends EventEmitter {
|
|
|
125863
125977
|
this.clearHealthPoll();
|
|
125864
125978
|
const processManager = this.engineProcess;
|
|
125865
125979
|
if (!processManager) {
|
|
125980
|
+
this.stopRequested = true;
|
|
125981
|
+
this.reachedRunningState = false;
|
|
125866
125982
|
this.lifecycleState = "stopped";
|
|
125867
125983
|
return;
|
|
125868
125984
|
}
|
|
@@ -125888,11 +126004,25 @@ class ModelManager extends EventEmitter {
|
|
|
125888
126004
|
get state() {
|
|
125889
126005
|
return this.lifecycleState;
|
|
125890
126006
|
}
|
|
126007
|
+
get resolvedServedModelName() {
|
|
126008
|
+
if (this.engine !== "custom")
|
|
126009
|
+
return null;
|
|
126010
|
+
return this.discoveredModelNames[0] ?? null;
|
|
126011
|
+
}
|
|
125891
126012
|
get wasRunning() {
|
|
125892
126013
|
return this.reachedRunningState;
|
|
125893
126014
|
}
|
|
126015
|
+
get customBaseURL() {
|
|
126016
|
+
if (this.engine !== "custom")
|
|
126017
|
+
return null;
|
|
126018
|
+
const baseUrl = this.engineConfig?.baseUrl;
|
|
126019
|
+
return typeof baseUrl === "string" && baseUrl.length > 0 ? baseUrl : null;
|
|
126020
|
+
}
|
|
125894
126021
|
async checkEngineReadiness() {
|
|
125895
126022
|
switch (this.engine) {
|
|
126023
|
+
case "custom": {
|
|
126024
|
+
return this.checkCustomReadiness();
|
|
126025
|
+
}
|
|
125896
126026
|
case "llama.cpp": {
|
|
125897
126027
|
return this.checkLlamacppReadiness();
|
|
125898
126028
|
}
|
|
@@ -125909,6 +126039,51 @@ class ModelManager extends EventEmitter {
|
|
|
125909
126039
|
return "ready";
|
|
125910
126040
|
}
|
|
125911
126041
|
}
|
|
126042
|
+
async checkCustomReadiness() {
|
|
126043
|
+
const baseURL = this.customBaseURL;
|
|
126044
|
+
if (!baseURL) {
|
|
126045
|
+
return "unreachable";
|
|
126046
|
+
}
|
|
126047
|
+
try {
|
|
126048
|
+
const response = await undiciExports.fetch(joinURL(baseURL, "/v1/models"), {
|
|
126049
|
+
method: "GET",
|
|
126050
|
+
signal: AbortSignal.timeout(5000)
|
|
126051
|
+
});
|
|
126052
|
+
if (response.status === 503) {
|
|
126053
|
+
return "loading";
|
|
126054
|
+
}
|
|
126055
|
+
if (!response.ok) {
|
|
126056
|
+
return "unreachable";
|
|
126057
|
+
}
|
|
126058
|
+
const payload = (await response.json());
|
|
126059
|
+
const models = Array.isArray(payload.data) ? payload.data : [];
|
|
126060
|
+
const modelIDs = models
|
|
126061
|
+
.map(model => {
|
|
126062
|
+
if (model === null || typeof model !== "object")
|
|
126063
|
+
return null;
|
|
126064
|
+
const id = model.id;
|
|
126065
|
+
return typeof id === "string" ? id : null;
|
|
126066
|
+
})
|
|
126067
|
+
.filter((id) => id !== null);
|
|
126068
|
+
if (modelIDs.length === 0) {
|
|
126069
|
+
this.logger.warn("Custom engine endpoint exposed no models via /v1/models", {
|
|
126070
|
+
engineBaseURL: baseURL
|
|
126071
|
+
});
|
|
126072
|
+
return "loading";
|
|
126073
|
+
}
|
|
126074
|
+
if (modelIDs.join("\n") !== this.discoveredModelNames.join("\n")) {
|
|
126075
|
+
this.logger.info("Discovered models on custom engine endpoint", {
|
|
126076
|
+
engineBaseURL: baseURL,
|
|
126077
|
+
models: modelIDs
|
|
126078
|
+
});
|
|
126079
|
+
this.discoveredModelNames = modelIDs;
|
|
126080
|
+
}
|
|
126081
|
+
return "ready";
|
|
126082
|
+
}
|
|
126083
|
+
catch (_error) {
|
|
126084
|
+
return "unreachable";
|
|
126085
|
+
}
|
|
126086
|
+
}
|
|
125912
126087
|
async checkGenericHealthReadiness() {
|
|
125913
126088
|
try {
|
|
125914
126089
|
const response = await undiciExports.fetch(joinURL(`http://localhost:${this.enginePort}`, "/health"), {
|
|
@@ -125964,18 +126139,24 @@ class ModelManager extends EventEmitter {
|
|
|
125964
126139
|
}
|
|
125965
126140
|
}
|
|
125966
126141
|
async waitForEngineReady() {
|
|
125967
|
-
const maxWaitMs = 15 * 60 * 1000;
|
|
126142
|
+
const maxWaitMs = Number.parseInt(process.env.ENGINE_STARTUP_TIMEOUT_MS ?? "", 10) || 15 * 60 * 1000;
|
|
125968
126143
|
const pollIntervalMs = 2000;
|
|
125969
126144
|
const start = Date.now();
|
|
125970
126145
|
while (Date.now() - start < maxWaitMs) {
|
|
125971
|
-
if (this.lifecycleState === "stopping") {
|
|
126146
|
+
if (this.lifecycleState === "stopping" || this.stopRequested) {
|
|
125972
126147
|
throw new Error("LLM engine startup interrupted by stop request");
|
|
125973
126148
|
}
|
|
125974
|
-
if (!this.engineProcess) {
|
|
126149
|
+
if (!this.engineProcess && this.engine !== "custom") {
|
|
125975
126150
|
throw new Error("LLM engine process exited before readiness checks completed");
|
|
125976
126151
|
}
|
|
125977
126152
|
const readiness = await this.checkEngineReadiness();
|
|
125978
126153
|
if (readiness === "ready") {
|
|
126154
|
+
// A stop() may have landed while the readiness request was
|
|
126155
|
+
// in flight; re-check before declaring ready.
|
|
126156
|
+
const lifecycleState = this.lifecycleState;
|
|
126157
|
+
if (lifecycleState === "stopping" || this.stopRequested) {
|
|
126158
|
+
throw new Error("LLM engine startup interrupted by stop request");
|
|
126159
|
+
}
|
|
125979
126160
|
return;
|
|
125980
126161
|
}
|
|
125981
126162
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
@@ -125993,19 +126174,32 @@ class ModelManager extends EventEmitter {
|
|
|
125993
126174
|
}
|
|
125994
126175
|
startHealthPoll() {
|
|
125995
126176
|
this.clearHealthPoll();
|
|
125996
|
-
this.logger.info("Starting background health poll
|
|
126177
|
+
this.logger.info("Starting background engine health poll", {
|
|
126178
|
+
agentEngineType: this.engine
|
|
126179
|
+
});
|
|
125997
126180
|
this.healthPollInterval = setInterval(() => {
|
|
125998
|
-
if (!this.engineProcess) {
|
|
126181
|
+
if (!this.engineProcess && this.engine !== "custom") {
|
|
125999
126182
|
this.clearHealthPoll();
|
|
126000
126183
|
return;
|
|
126001
126184
|
}
|
|
126002
126185
|
this.checkEngineReadiness()
|
|
126003
126186
|
.then(readiness => {
|
|
126004
126187
|
if (readiness === "ready") {
|
|
126005
|
-
this.
|
|
126006
|
-
|
|
126007
|
-
|
|
126008
|
-
|
|
126188
|
+
if (this.lifecycleState === "errored" ||
|
|
126189
|
+
this.lifecycleState === "starting") {
|
|
126190
|
+
this.lifecycleState = "running";
|
|
126191
|
+
this.reachedRunningState = true;
|
|
126192
|
+
this.emit("engineReady");
|
|
126193
|
+
}
|
|
126194
|
+
if (this.engine !== "custom") {
|
|
126195
|
+
this.clearHealthPoll();
|
|
126196
|
+
}
|
|
126197
|
+
return;
|
|
126198
|
+
}
|
|
126199
|
+
if (this.engine === "custom" &&
|
|
126200
|
+
readiness === "unreachable" &&
|
|
126201
|
+
this.lifecycleState === "running") {
|
|
126202
|
+
this.recordEngineError(new Error(`Custom engine endpoint unreachable: ${this.customBaseURL}`));
|
|
126009
126203
|
}
|
|
126010
126204
|
})
|
|
126011
126205
|
.catch(() => {
|
|
@@ -126030,6 +126224,15 @@ class ModelManager extends EventEmitter {
|
|
|
126030
126224
|
this.lastEngineError = err;
|
|
126031
126225
|
this.emit("engineError", err);
|
|
126032
126226
|
}
|
|
126227
|
+
requireCustomBaseURL() {
|
|
126228
|
+
const baseURL = this.customBaseURL;
|
|
126229
|
+
if (!baseURL) {
|
|
126230
|
+
throw new ConfigurationInvalidError({
|
|
126231
|
+
message: "Custom engine requires a base URL"
|
|
126232
|
+
});
|
|
126233
|
+
}
|
|
126234
|
+
return baseURL;
|
|
126235
|
+
}
|
|
126033
126236
|
async releaseDownloadLock() {
|
|
126034
126237
|
const handle = this.downloadLockHandle;
|
|
126035
126238
|
if (!handle)
|
|
@@ -126096,6 +126299,8 @@ class ModelManager extends EventEmitter {
|
|
|
126096
126299
|
async startEngineProcess() {
|
|
126097
126300
|
const targetDir = join(this.modelsDirectory, this.uniqueName);
|
|
126098
126301
|
switch (this.engine) {
|
|
126302
|
+
case "custom":
|
|
126303
|
+
return null;
|
|
126099
126304
|
case "exllamav3":
|
|
126100
126305
|
return startExllamav3.call(this, {
|
|
126101
126306
|
enginePort: this.enginePort,
|
|
@@ -126407,8 +126612,9 @@ function isEngineUsageChunk(value) {
|
|
|
126407
126612
|
}
|
|
126408
126613
|
return true;
|
|
126409
126614
|
}
|
|
126410
|
-
function monitorEngineResponseStream({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt }) {
|
|
126615
|
+
function monitorEngineResponseStream({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt, responseModelName }) {
|
|
126411
126616
|
const startedAt = requestStartedAt ?? Date.now();
|
|
126617
|
+
const clientModelName = responseModelName ?? null;
|
|
126412
126618
|
const passThrough = new PassThrough();
|
|
126413
126619
|
passThrough.on("error", (error) => {
|
|
126414
126620
|
logger.error("Engine response stream error", {
|
|
@@ -126420,53 +126626,61 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126420
126626
|
let firstChunkAt = null;
|
|
126421
126627
|
let usage = null;
|
|
126422
126628
|
let buffer = "";
|
|
126629
|
+
let pendingFragment = "";
|
|
126423
126630
|
let completed = false;
|
|
126424
|
-
function
|
|
126425
|
-
const
|
|
126426
|
-
|
|
126427
|
-
|
|
126428
|
-
|
|
126429
|
-
|
|
126430
|
-
|
|
126431
|
-
|
|
126432
|
-
|
|
126631
|
+
function rewriteDataLine(rawLine) {
|
|
126632
|
+
const line = rawLine.trim();
|
|
126633
|
+
if (!line.startsWith("data:")) {
|
|
126634
|
+
return rawLine;
|
|
126635
|
+
}
|
|
126636
|
+
const payload = line.slice(5).trim();
|
|
126637
|
+
if (!payload || payload === "[DONE]") {
|
|
126638
|
+
return rawLine;
|
|
126639
|
+
}
|
|
126640
|
+
try {
|
|
126641
|
+
const parsed = JSON.parse(payload);
|
|
126642
|
+
let modified = false;
|
|
126643
|
+
if (coerceToolCallArguments(parsed)) {
|
|
126644
|
+
modified = true;
|
|
126433
126645
|
}
|
|
126434
|
-
|
|
126435
|
-
|
|
126436
|
-
|
|
126437
|
-
|
|
126646
|
+
if (clientModelName !== null &&
|
|
126647
|
+
typeof parsed.model === "string" &&
|
|
126648
|
+
parsed.model !== clientModelName) {
|
|
126649
|
+
parsed.model = clientModelName;
|
|
126650
|
+
modified = true;
|
|
126438
126651
|
}
|
|
126439
|
-
|
|
126440
|
-
const
|
|
126441
|
-
|
|
126442
|
-
|
|
126652
|
+
if (parsed.usage) {
|
|
126653
|
+
const usageChunk = parsed.usage;
|
|
126654
|
+
const effectiveContext = getEffectiveContextLength({
|
|
126655
|
+
contextLength,
|
|
126656
|
+
engineConfig,
|
|
126657
|
+
engineType
|
|
126658
|
+
});
|
|
126659
|
+
if (usageChunk.context_usage === undefined &&
|
|
126660
|
+
usageChunk.prompt_tokens !== undefined &&
|
|
126661
|
+
effectiveContext !== null) {
|
|
126662
|
+
usageChunk.context_usage = usageChunk.prompt_tokens / effectiveContext;
|
|
126443
126663
|
modified = true;
|
|
126444
126664
|
}
|
|
126445
|
-
if (parsed.usage) {
|
|
126446
|
-
const usageChunk = parsed.usage;
|
|
126447
|
-
const effectiveContext = getEffectiveContextLength({
|
|
126448
|
-
contextLength,
|
|
126449
|
-
engineConfig,
|
|
126450
|
-
engineType
|
|
126451
|
-
});
|
|
126452
|
-
if (usageChunk.context_usage === undefined &&
|
|
126453
|
-
usageChunk.prompt_tokens !== undefined &&
|
|
126454
|
-
effectiveContext !== null) {
|
|
126455
|
-
usageChunk.context_usage = usageChunk.prompt_tokens / effectiveContext;
|
|
126456
|
-
modified = true;
|
|
126457
|
-
}
|
|
126458
|
-
}
|
|
126459
|
-
if (modified) {
|
|
126460
|
-
modifiedLines.push("data: " + JSON.stringify(parsed));
|
|
126461
|
-
continue;
|
|
126462
|
-
}
|
|
126463
126665
|
}
|
|
126464
|
-
|
|
126465
|
-
|
|
126666
|
+
if (modified) {
|
|
126667
|
+
return "data: " + JSON.stringify(parsed);
|
|
126466
126668
|
}
|
|
126467
|
-
modifiedLines.push(rawLine);
|
|
126468
126669
|
}
|
|
126469
|
-
|
|
126670
|
+
catch (_error) {
|
|
126671
|
+
// Ignore malformed chunks
|
|
126672
|
+
}
|
|
126673
|
+
return rawLine;
|
|
126674
|
+
}
|
|
126675
|
+
// SSE events can split across transport chunks: hold back the trailing
|
|
126676
|
+
// (newline-less) fragment and only rewrite complete data lines, so a
|
|
126677
|
+
// partial JSON event is never forwarded with its upstream model name.
|
|
126678
|
+
function modifyChunkWithUsage(chunk, flush = false) {
|
|
126679
|
+
const combined = pendingFragment + chunk.toString("utf8");
|
|
126680
|
+
const lines = combined.split("\n");
|
|
126681
|
+
pendingFragment = flush ? "" : (lines.pop() ?? "");
|
|
126682
|
+
const modifiedLines = lines.map(rewriteDataLine);
|
|
126683
|
+
return Buffer.from(modifiedLines.length > 0 ? modifiedLines.join("\n") + (flush ? "" : "\n") : "", "utf8");
|
|
126470
126684
|
}
|
|
126471
126685
|
function parseUsageFromBuffer() {
|
|
126472
126686
|
const lines = buffer.split("\n");
|
|
@@ -126562,6 +126776,9 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126562
126776
|
if (buffer.length > 0) {
|
|
126563
126777
|
parseUsageFromBuffer();
|
|
126564
126778
|
}
|
|
126779
|
+
if (pendingFragment.length > 0) {
|
|
126780
|
+
passThrough.write(modifyChunkWithUsage(Buffer.from("", "utf8"), true));
|
|
126781
|
+
}
|
|
126565
126782
|
logEngineMetrics({
|
|
126566
126783
|
agentEngineType,
|
|
126567
126784
|
level: "info",
|
|
@@ -126604,9 +126821,11 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126604
126821
|
stream: passThrough
|
|
126605
126822
|
};
|
|
126606
126823
|
}
|
|
126607
|
-
function monitorEngineResponseSingle({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt }) {
|
|
126824
|
+
function monitorEngineResponseSingle({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt, responseModelName }) {
|
|
126608
126825
|
const maxUsageCaptureBytes = 1024 * 1024;
|
|
126609
126826
|
const startedAt = requestStartedAt ?? Date.now();
|
|
126827
|
+
const clientModelName = responseModelName ?? null;
|
|
126828
|
+
const rewriteBuffer = clientModelName !== null ? [] : null;
|
|
126610
126829
|
const passThrough = new PassThrough();
|
|
126611
126830
|
passThrough.on("error", (error) => {
|
|
126612
126831
|
logger.error("Engine response stream error", {
|
|
@@ -126662,7 +126881,12 @@ function monitorEngineResponseSingle({ agentEngineType, body, contextLength, eng
|
|
|
126662
126881
|
usageChunks.length = 0;
|
|
126663
126882
|
}
|
|
126664
126883
|
}
|
|
126665
|
-
|
|
126884
|
+
if (rewriteBuffer) {
|
|
126885
|
+
rewriteBuffer.push(chunkBuffer);
|
|
126886
|
+
}
|
|
126887
|
+
else {
|
|
126888
|
+
passThrough.write(chunkBuffer);
|
|
126889
|
+
}
|
|
126666
126890
|
});
|
|
126667
126891
|
body.once("error", err => {
|
|
126668
126892
|
logEngineMetrics({
|
|
@@ -126722,6 +126946,24 @@ function monitorEngineResponseSingle({ agentEngineType, body, contextLength, eng
|
|
|
126722
126946
|
responseBytes,
|
|
126723
126947
|
usage
|
|
126724
126948
|
});
|
|
126949
|
+
if (rewriteBuffer) {
|
|
126950
|
+
const original = Buffer.concat(rewriteBuffer);
|
|
126951
|
+
let output = original;
|
|
126952
|
+
try {
|
|
126953
|
+
const parsed = JSON.parse(original.toString("utf8"));
|
|
126954
|
+
if (parsed !== null &&
|
|
126955
|
+
typeof parsed === "object" &&
|
|
126956
|
+
typeof parsed.model === "string" &&
|
|
126957
|
+
parsed.model !== clientModelName) {
|
|
126958
|
+
parsed.model = clientModelName;
|
|
126959
|
+
output = Buffer.from(JSON.stringify(parsed), "utf8");
|
|
126960
|
+
}
|
|
126961
|
+
}
|
|
126962
|
+
catch (_error) {
|
|
126963
|
+
// Non-JSON body: pass through untouched
|
|
126964
|
+
}
|
|
126965
|
+
passThrough.write(output);
|
|
126966
|
+
}
|
|
126725
126967
|
finalize(null);
|
|
126726
126968
|
passThrough.end();
|
|
126727
126969
|
});
|
|
@@ -126858,7 +127100,7 @@ function applyChatTemplateKwargs({ body, model }) {
|
|
|
126858
127100
|
}
|
|
126859
127101
|
return payload;
|
|
126860
127102
|
}
|
|
126861
|
-
function serializeRequestBody$1(body, { model, path } = {}) {
|
|
127103
|
+
function serializeRequestBody$1(body, { model, path, servedModelName } = {}) {
|
|
126862
127104
|
if (!isPlainObject$a(body)) {
|
|
126863
127105
|
const payload = typeof body === "string" ? body : JSON.stringify(body);
|
|
126864
127106
|
return {
|
|
@@ -126867,6 +127109,9 @@ function serializeRequestBody$1(body, { model, path } = {}) {
|
|
|
126867
127109
|
};
|
|
126868
127110
|
}
|
|
126869
127111
|
let requestPayload = { ...body };
|
|
127112
|
+
if (servedModelName) {
|
|
127113
|
+
requestPayload.model = servedModelName;
|
|
127114
|
+
}
|
|
126870
127115
|
if (path === "/v1/chat/completions" && model) {
|
|
126871
127116
|
requestPayload = applyChatTemplateKwargs({ body: requestPayload, model });
|
|
126872
127117
|
}
|
|
@@ -126923,8 +127168,15 @@ async function proxyEmbeddingsRoute({ body, conduitConfiguration, endpointId, lo
|
|
|
126923
127168
|
}
|
|
126924
127169
|
const engineType = conduitConfiguration.engineConfig?.type ?? null;
|
|
126925
127170
|
const engineConfig = conduitConfiguration.engineConfig ?? null;
|
|
127171
|
+
const servedModelName = modelManager.resolvedServedModelName;
|
|
127172
|
+
const clientModelName = isPlainObject$a(body) && typeof body.model === "string" ? body.model : null;
|
|
126926
127173
|
const serializedBody = isPlainObject$a(body)
|
|
126927
|
-
? JSON.stringify(
|
|
127174
|
+
? JSON.stringify(servedModelName
|
|
127175
|
+
? {
|
|
127176
|
+
...body,
|
|
127177
|
+
model: servedModelName
|
|
127178
|
+
}
|
|
127179
|
+
: body)
|
|
126928
127180
|
: typeof body === "string"
|
|
126929
127181
|
? body
|
|
126930
127182
|
: JSON.stringify(body);
|
|
@@ -127042,7 +127294,8 @@ async function proxyEmbeddingsRoute({ body, conduitConfiguration, endpointId, lo
|
|
|
127042
127294
|
onComplete: onMonitoringComplete,
|
|
127043
127295
|
requestBodyBytes,
|
|
127044
127296
|
requestPath: "/v1/embeddings",
|
|
127045
|
-
requestStartedAt
|
|
127297
|
+
requestStartedAt,
|
|
127298
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127046
127299
|
});
|
|
127047
127300
|
return {
|
|
127048
127301
|
body: monitoredResponse.stream,
|
|
@@ -127067,8 +127320,10 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127067
127320
|
}
|
|
127068
127321
|
const engineType = conduitConfiguration.engineConfig?.type ?? null;
|
|
127069
127322
|
const engineConfig = conduitConfiguration.engineConfig ?? null;
|
|
127323
|
+
const servedModelName = modelManager.resolvedServedModelName;
|
|
127324
|
+
const clientModelName = isPlainObject$a(body) && typeof body.model === "string" ? body.model : null;
|
|
127070
127325
|
const effectiveBody = modelManager.model.multimodalEnabled ? body : stripImagesFromBody(body);
|
|
127071
|
-
const { bytes: requestBodyBytes, payload: serializedBody } = serializeRequestBody$1(effectiveBody, { model: modelManager.model, path });
|
|
127326
|
+
const { bytes: requestBodyBytes, payload: serializedBody } = serializeRequestBody$1(effectiveBody, { model: modelManager.model, path, servedModelName });
|
|
127072
127327
|
const requestStartedAt = Date.now();
|
|
127073
127328
|
const requestBody = JSON.parse(serializedBody);
|
|
127074
127329
|
const streamRequested = requestBody.stream === true;
|
|
@@ -127208,7 +127463,8 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127208
127463
|
onComplete: onMonitoringComplete,
|
|
127209
127464
|
requestBodyBytes,
|
|
127210
127465
|
requestPath: path,
|
|
127211
|
-
requestStartedAt
|
|
127466
|
+
requestStartedAt,
|
|
127467
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127212
127468
|
})
|
|
127213
127469
|
: monitorEngineResponseSingle({
|
|
127214
127470
|
agentEngineType: engineType ?? "unknown",
|
|
@@ -127220,7 +127476,8 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127220
127476
|
onComplete: onMonitoringComplete,
|
|
127221
127477
|
requestBodyBytes,
|
|
127222
127478
|
requestPath: path,
|
|
127223
|
-
requestStartedAt
|
|
127479
|
+
requestStartedAt,
|
|
127480
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127224
127481
|
});
|
|
127225
127482
|
return {
|
|
127226
127483
|
body: monitoredResponse.stream,
|
|
@@ -138514,13 +138771,16 @@ async function detectDockerVersion() {
|
|
|
138514
138771
|
* Flattens flat CLI extra-arg tokens into [arg, value] pairs, sorted by ARG NAME (ascending, ties by
|
|
138515
138772
|
* value). `--flag=value` pairs split on the first `=`; a bare `--flag` consumes the following token
|
|
138516
138773
|
* as its value when that token does not start with "-" (classic CLI convention); anything else
|
|
138517
|
-
* (flags, non-strings) is dropped.
|
|
138774
|
+
* (flags, non-strings) is dropped. Secret-like option values are masked before pairing so they
|
|
138775
|
+
* never reach execution reports.
|
|
138518
138776
|
*/
|
|
138519
138777
|
function pairExtraArgs(tokens) {
|
|
138520
138778
|
if (!Array.isArray(tokens)) {
|
|
138521
138779
|
return [];
|
|
138522
138780
|
}
|
|
138523
|
-
|
|
138781
|
+
// Non-string tokens become empty strings: they keep their position as a
|
|
138782
|
+
// non-consumable barrier while allowing secret masking over string tokens.
|
|
138783
|
+
const list = redactSecretArgs(tokens.map(token => (typeof token === "string" ? token : "")));
|
|
138524
138784
|
const pairs = [];
|
|
138525
138785
|
let index = 0;
|
|
138526
138786
|
while (index < list.length) {
|
|
@@ -138656,9 +138916,9 @@ async function createApplication({ abortController, apiClient, configuration, lo
|
|
|
138656
138916
|
}
|
|
138657
138917
|
const reporter = new EngineExecutionReporter({
|
|
138658
138918
|
buildContext: () => {
|
|
138659
|
-
const engineType =
|
|
138660
|
-
"llama.cpp");
|
|
138919
|
+
const engineType = conduitConfiguration.engineConfig?.type ?? "llama.cpp";
|
|
138661
138920
|
const versions = {
|
|
138921
|
+
custom: null,
|
|
138662
138922
|
exllamav3: machine?.exllamav3Version ?? null,
|
|
138663
138923
|
"llama.cpp": machine?.llamaCppVersion ?? null,
|
|
138664
138924
|
"mlx-lm": machine?.mlxlmVersion ?? null,
|
|
@@ -139145,7 +139405,11 @@ function createModelManagerFromConfig(conduitConfiguration, configuration, logge
|
|
|
139145
139405
|
return new ModelManager({
|
|
139146
139406
|
contextLength: conduitConfiguration.contextLength ?? null,
|
|
139147
139407
|
engineConfig: engineConfig
|
|
139148
|
-
? {
|
|
139408
|
+
? {
|
|
139409
|
+
baseUrl: engineConfig.baseUrl ?? null,
|
|
139410
|
+
extraArgs: engineConfig.extraArgs,
|
|
139411
|
+
type: engineConfig.type
|
|
139412
|
+
}
|
|
139149
139413
|
: null,
|
|
139150
139414
|
enginePort: configuration.enginePort,
|
|
139151
139415
|
engineType: engineConfig?.type ?? "llama.cpp",
|
|
@@ -139156,7 +139420,11 @@ function createModelManagerFromConfig(conduitConfiguration, configuration, logge
|
|
|
139156
139420
|
}
|
|
139157
139421
|
function getConduitModelFileName(configuration) {
|
|
139158
139422
|
const { source } = configuration.targetModel;
|
|
139159
|
-
|
|
139423
|
+
if (source.type === "huggingface")
|
|
139424
|
+
return source.slug;
|
|
139425
|
+
if (source.type === "storage")
|
|
139426
|
+
return source.irid;
|
|
139427
|
+
return configuration.targetModel.id;
|
|
139160
139428
|
}
|
|
139161
139429
|
function getConduitModelName(configuration) {
|
|
139162
139430
|
return configuration.targetModel.id;
|