@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.sea.cjs
CHANGED
|
@@ -19900,6 +19900,7 @@ object$5({
|
|
|
19900
19900
|
});
|
|
19901
19901
|
|
|
19902
19902
|
const LLMEngineSchema = _enum$1([
|
|
19903
|
+
"custom",
|
|
19903
19904
|
"exllamav3",
|
|
19904
19905
|
"llama.cpp",
|
|
19905
19906
|
"mlx-lm",
|
|
@@ -19908,8 +19909,25 @@ const LLMEngineSchema = _enum$1([
|
|
|
19908
19909
|
"vllm"
|
|
19909
19910
|
]);
|
|
19910
19911
|
const EngineConfigSchema = object$5({
|
|
19912
|
+
baseUrl: string$2().url().nullable().default(null),
|
|
19911
19913
|
extraArgs: array$1(string$2()),
|
|
19912
19914
|
type: LLMEngineSchema
|
|
19915
|
+
})
|
|
19916
|
+
.superRefine((config, ctx) => {
|
|
19917
|
+
if (config.type === "custom" && config.baseUrl === null) {
|
|
19918
|
+
ctx.addIssue({
|
|
19919
|
+
code: "custom",
|
|
19920
|
+
message: "Engine type 'custom' requires a base URL",
|
|
19921
|
+
path: ["baseUrl"]
|
|
19922
|
+
});
|
|
19923
|
+
}
|
|
19924
|
+
if (config.type !== "custom" && config.baseUrl !== null) {
|
|
19925
|
+
ctx.addIssue({
|
|
19926
|
+
code: "custom",
|
|
19927
|
+
message: "Base URL is only valid for engine type 'custom'",
|
|
19928
|
+
path: ["baseUrl"]
|
|
19929
|
+
});
|
|
19930
|
+
}
|
|
19913
19931
|
});
|
|
19914
19932
|
const LLMModelFormatSchema = _enum$1([
|
|
19915
19933
|
// VLLM / SGLang / TensorRT-LLM
|
|
@@ -19968,14 +19986,18 @@ const LLMModelSchema = object$5({
|
|
|
19968
19986
|
id: string$2().min(1),
|
|
19969
19987
|
multimodalEnabled: boolean$1(),
|
|
19970
19988
|
source: discriminatedUnion("type", [
|
|
19989
|
+
// Custom engines: no local model record, serving is external
|
|
19971
19990
|
object$5({
|
|
19972
|
-
|
|
19973
|
-
type: literal("storage")
|
|
19991
|
+
type: literal("external")
|
|
19974
19992
|
}),
|
|
19975
19993
|
object$5({
|
|
19976
19994
|
modelSecret: string$2().min(1).nullable(),
|
|
19977
19995
|
slug: string$2().min(1),
|
|
19978
19996
|
type: literal("huggingface")
|
|
19997
|
+
}),
|
|
19998
|
+
object$5({
|
|
19999
|
+
irid: IRIDSchema,
|
|
20000
|
+
type: literal("storage")
|
|
19979
20001
|
})
|
|
19980
20002
|
]),
|
|
19981
20003
|
taskType: LLMModelTaskTypeSchema,
|
|
@@ -21119,7 +21141,8 @@ const CreateModelResponseSchema = object$5({
|
|
|
21119
21141
|
const CreateSourceBodySchema = object$5({
|
|
21120
21142
|
contextLength: number$1().int().positive().max(1048576).optional(),
|
|
21121
21143
|
engineId: ULIDSchema,
|
|
21122
|
-
|
|
21144
|
+
// Optional for custom engines, which serve externally managed models
|
|
21145
|
+
modelID: ULIDSchema.nullable().optional(),
|
|
21123
21146
|
name: ResourceNameSchema,
|
|
21124
21147
|
quantizationLabel: string$2().min(1).max(128).optional()
|
|
21125
21148
|
});
|
|
@@ -21168,7 +21191,7 @@ const SourceDetailResponseSchema = object$5({
|
|
|
21168
21191
|
const UpdateSourceBodySchema = object$5({
|
|
21169
21192
|
contextLength: number$1().int().positive().nullable().optional(),
|
|
21170
21193
|
engineId: ULIDSchema.nullable().optional(),
|
|
21171
|
-
modelID: ULIDSchema.optional(),
|
|
21194
|
+
modelID: ULIDSchema.nullable().optional(),
|
|
21172
21195
|
name: ResourceNameSchema.optional(),
|
|
21173
21196
|
quantizationLabel: string$2().min(1).max(128).nullable().optional()
|
|
21174
21197
|
});
|
|
@@ -21292,6 +21315,7 @@ const CreateEndpointResponseSchema = object$5({
|
|
|
21292
21315
|
id: ULIDSchema
|
|
21293
21316
|
});
|
|
21294
21317
|
const EngineOutputSchema = object$5({
|
|
21318
|
+
baseUrl: string$2().nullable(),
|
|
21295
21319
|
created: string$2(),
|
|
21296
21320
|
extraArgs: array$1(string$2()),
|
|
21297
21321
|
id: ULIDSchema,
|
|
@@ -21300,11 +21324,29 @@ const EngineOutputSchema = object$5({
|
|
|
21300
21324
|
updated: string$2()
|
|
21301
21325
|
});
|
|
21302
21326
|
const CreateEngineBodySchema = object$5({
|
|
21327
|
+
baseUrl: string$2().url().nullable().optional(),
|
|
21303
21328
|
extraArgs: array$1(string$2()).optional(),
|
|
21304
21329
|
name: ResourceNameSchema,
|
|
21305
21330
|
type: LLMEngineSchema
|
|
21331
|
+
})
|
|
21332
|
+
.superRefine((body, ctx) => {
|
|
21333
|
+
if (body.type === "custom" && !body.baseUrl) {
|
|
21334
|
+
ctx.addIssue({
|
|
21335
|
+
code: "custom",
|
|
21336
|
+
message: "Engine type 'custom' requires a base URL",
|
|
21337
|
+
path: ["baseUrl"]
|
|
21338
|
+
});
|
|
21339
|
+
}
|
|
21340
|
+
if (body.type !== "custom" && body.baseUrl) {
|
|
21341
|
+
ctx.addIssue({
|
|
21342
|
+
code: "custom",
|
|
21343
|
+
message: "Base URL is only valid for engine type 'custom'",
|
|
21344
|
+
path: ["baseUrl"]
|
|
21345
|
+
});
|
|
21346
|
+
}
|
|
21306
21347
|
});
|
|
21307
21348
|
const UpdateEngineBodySchema = object$5({
|
|
21349
|
+
baseUrl: string$2().url().nullable().optional(),
|
|
21308
21350
|
extraArgs: array$1(string$2()).optional(),
|
|
21309
21351
|
name: ResourceNameSchema.optional(),
|
|
21310
21352
|
type: LLMEngineSchema.optional()
|
|
@@ -22110,6 +22152,11 @@ const RecommendedModelSchema = object$5({
|
|
|
22110
22152
|
const recommendedModels = RecommendedModelSchema.array().parse(modelsData);
|
|
22111
22153
|
|
|
22112
22154
|
const ENGINE_API_COMPATIBILITY = {
|
|
22155
|
+
custom: {
|
|
22156
|
+
nativeAnthropicMessages: false,
|
|
22157
|
+
supportsEmbeddings: true,
|
|
22158
|
+
supportsVision: true
|
|
22159
|
+
},
|
|
22113
22160
|
exllamav3: {
|
|
22114
22161
|
nativeAnthropicMessages: false,
|
|
22115
22162
|
supportsEmbeddings: false,
|
|
@@ -111759,6 +111806,27 @@ function registerEndpointCommands({ program }) {
|
|
|
111759
111806
|
}
|
|
111760
111807
|
|
|
111761
111808
|
const ENGINE_TYPES = LLMEngineSchema.options;
|
|
111809
|
+
function isValidURL(value) {
|
|
111810
|
+
try {
|
|
111811
|
+
const url = new URL(value);
|
|
111812
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
111813
|
+
}
|
|
111814
|
+
catch (_error) {
|
|
111815
|
+
return false;
|
|
111816
|
+
}
|
|
111817
|
+
}
|
|
111818
|
+
function validateEngineBaseURL({ baseUrl, type }) {
|
|
111819
|
+
if (type === "custom" && !baseUrl) {
|
|
111820
|
+
throw new Error("Engine type 'custom' requires --base-url");
|
|
111821
|
+
}
|
|
111822
|
+
if (baseUrl !== undefined && type !== "custom") {
|
|
111823
|
+
throw new Error("--base-url is only valid for engine type 'custom'");
|
|
111824
|
+
}
|
|
111825
|
+
if (baseUrl !== undefined && baseUrl !== "" && !isValidURL(baseUrl)) {
|
|
111826
|
+
throw new Error(`Invalid --base-url value: ${baseUrl}`);
|
|
111827
|
+
}
|
|
111828
|
+
return baseUrl === "" ? null : (baseUrl ?? null);
|
|
111829
|
+
}
|
|
111762
111830
|
function buildEngineCreateBody(options) {
|
|
111763
111831
|
if (!options.name) {
|
|
111764
111832
|
throw new Error("--name is required");
|
|
@@ -111769,7 +111837,12 @@ function buildEngineCreateBody(options) {
|
|
|
111769
111837
|
if (!ENGINE_TYPES.includes(options.type)) {
|
|
111770
111838
|
throw new Error(`Invalid engine type: ${options.type} (expected one of: ${ENGINE_TYPES.join(", ")})`);
|
|
111771
111839
|
}
|
|
111840
|
+
const baseUrl = validateEngineBaseURL({
|
|
111841
|
+
baseUrl: options.baseUrl,
|
|
111842
|
+
type: options.type
|
|
111843
|
+
});
|
|
111772
111844
|
return {
|
|
111845
|
+
baseUrl,
|
|
111773
111846
|
extraArgs: options.arg ?? [],
|
|
111774
111847
|
name: options.name,
|
|
111775
111848
|
type: options.type
|
|
@@ -111787,6 +111860,24 @@ function buildEngineUpdateBody(options) {
|
|
|
111787
111860
|
}
|
|
111788
111861
|
if (options.arg !== undefined)
|
|
111789
111862
|
body.extraArgs = options.arg;
|
|
111863
|
+
if (options.baseUrl !== undefined) {
|
|
111864
|
+
const targetType = body.type;
|
|
111865
|
+
if (targetType !== undefined) {
|
|
111866
|
+
body.baseUrl = validateEngineBaseURL({
|
|
111867
|
+
baseUrl: options.baseUrl,
|
|
111868
|
+
type: targetType
|
|
111869
|
+
});
|
|
111870
|
+
}
|
|
111871
|
+
else if (options.baseUrl === "") {
|
|
111872
|
+
body.baseUrl = null;
|
|
111873
|
+
}
|
|
111874
|
+
else if (!isValidURL(options.baseUrl)) {
|
|
111875
|
+
throw new Error(`Invalid --base-url value: ${options.baseUrl}`);
|
|
111876
|
+
}
|
|
111877
|
+
else {
|
|
111878
|
+
body.baseUrl = options.baseUrl;
|
|
111879
|
+
}
|
|
111880
|
+
}
|
|
111790
111881
|
return body;
|
|
111791
111882
|
}
|
|
111792
111883
|
|
|
@@ -111800,10 +111891,11 @@ function registerEngineCommands({ program }) {
|
|
|
111800
111891
|
.description("Create or update an inference engine resource")
|
|
111801
111892
|
.option("--api-url <url>", "API base URL (required, no environment variable fallback)")
|
|
111802
111893
|
.option("--arg <flag>", 'Raw engine CLI flag, repeatable (eg --arg "--flash-attn on")', collect, [])
|
|
111894
|
+
.option("--base-url <url>", "Base URL of an external OpenAI-compatible server (engine type 'custom' only)")
|
|
111803
111895
|
.option("--id <ulid>", "Target an existing engine by ID (requires --update)")
|
|
111804
111896
|
.option("--key <value>", "API key (required, no environment variable fallback)")
|
|
111805
111897
|
.option("--name <name>", "Engine name (matched by --update)")
|
|
111806
|
-
.option("--type <type>", "Engine type: llama.cpp|vllm|sglang|tensorrt-llm|mlx-lm|exllamav3")
|
|
111898
|
+
.option("--type <type>", "Engine type: llama.cpp|vllm|sglang|tensorrt-llm|mlx-lm|exllamav3|custom")
|
|
111807
111899
|
.option("--update", "Update an existing engine matched by name (or --id) instead of erroring")
|
|
111808
111900
|
.action(async (options) => {
|
|
111809
111901
|
const { apiURL, apiKey } = resolveManagementConnection(options);
|
|
@@ -117803,6 +117895,9 @@ async function getChatTemplateEngineArgs({ engine, model, targetDirectory }) {
|
|
|
117803
117895
|
return [];
|
|
117804
117896
|
const flag = FLAG_BASED_ENGINE_ARGS[engine];
|
|
117805
117897
|
if (!flag) {
|
|
117898
|
+
if (engine === "custom") {
|
|
117899
|
+
console.warn("[chatTemplate] Custom engines manage their own serving; ignoring chat template override");
|
|
117900
|
+
}
|
|
117806
117901
|
if (engine === "tensorrt-llm") {
|
|
117807
117902
|
console.warn("[chatTemplate] TensorRT-LLM does not support chat template overrides; ignoring");
|
|
117808
117903
|
}
|
|
@@ -124700,7 +124795,7 @@ function matchesQuantizationVariant({ filePath, variant }) {
|
|
|
124700
124795
|
return segments.slice(0, -1).some(segment => matcher.test(segment));
|
|
124701
124796
|
}
|
|
124702
124797
|
async function findQuantizedModelTarget({ model, path }) {
|
|
124703
|
-
if (model.source.type
|
|
124798
|
+
if (model.source.type !== "huggingface") {
|
|
124704
124799
|
throw new Error("Model storage not supported yet");
|
|
124705
124800
|
}
|
|
124706
124801
|
if (model.format !== "gguf") {
|
|
@@ -125615,7 +125710,11 @@ function sanitizeSegment(value) {
|
|
|
125615
125710
|
.replace(new RegExp(`${SEPARATOR}{2,}`, "g"), SEPARATOR);
|
|
125616
125711
|
}
|
|
125617
125712
|
function createModelStorageKey(model) {
|
|
125618
|
-
const identifier = model.source.type === "huggingface"
|
|
125713
|
+
const identifier = model.source.type === "huggingface"
|
|
125714
|
+
? model.source.slug
|
|
125715
|
+
: model.source.type === "storage"
|
|
125716
|
+
? model.source.irid
|
|
125717
|
+
: model.id;
|
|
125619
125718
|
return `${model.source.type}${SEPARATOR}${sanitizeSegment(identifier)}`;
|
|
125620
125719
|
}
|
|
125621
125720
|
|
|
@@ -125691,6 +125790,7 @@ class ModelManager extends EventEmitter {
|
|
|
125691
125790
|
uniqueName;
|
|
125692
125791
|
contextLength;
|
|
125693
125792
|
logger;
|
|
125793
|
+
discoveredModelNames = [];
|
|
125694
125794
|
engineProcess = null;
|
|
125695
125795
|
healthPollInterval = null;
|
|
125696
125796
|
lastEngineError = null;
|
|
@@ -125723,6 +125823,7 @@ class ModelManager extends EventEmitter {
|
|
|
125723
125823
|
}
|
|
125724
125824
|
async fetchOpenAI(path, opts) {
|
|
125725
125825
|
switch (this.engine) {
|
|
125826
|
+
case "custom":
|
|
125726
125827
|
case "exllamav3":
|
|
125727
125828
|
case "llama.cpp":
|
|
125728
125829
|
case "mlx-lm":
|
|
@@ -125730,6 +125831,9 @@ class ModelManager extends EventEmitter {
|
|
|
125730
125831
|
case "tensorrt-llm":
|
|
125731
125832
|
case "vllm": {
|
|
125732
125833
|
this.logger.debug(`Fetching from engine: ${path}`);
|
|
125834
|
+
const baseURL = this.engine === "custom"
|
|
125835
|
+
? this.requireCustomBaseURL()
|
|
125836
|
+
: `http://localhost:${this.enginePort}`;
|
|
125733
125837
|
const callerSignal = opts?.signal;
|
|
125734
125838
|
const controller = new AbortController();
|
|
125735
125839
|
const timeout = setTimeout(() => {
|
|
@@ -125740,7 +125844,7 @@ class ModelManager extends EventEmitter {
|
|
|
125740
125844
|
: controller.signal;
|
|
125741
125845
|
try {
|
|
125742
125846
|
const fetchStartedAt = Date.now();
|
|
125743
|
-
const response = await undiciExports.fetch(joinURL(
|
|
125847
|
+
const response = await undiciExports.fetch(joinURL(baseURL, path), {
|
|
125744
125848
|
...opts,
|
|
125745
125849
|
dispatcher: ENGINE_AGENT,
|
|
125746
125850
|
headers: {
|
|
@@ -125775,6 +125879,11 @@ class ModelManager extends EventEmitter {
|
|
|
125775
125879
|
modelID: this.model.id
|
|
125776
125880
|
});
|
|
125777
125881
|
switch (this.engine) {
|
|
125882
|
+
case "custom":
|
|
125883
|
+
if (this.model.chatTemplate) {
|
|
125884
|
+
this.logger.warn("Chat template overrides are ignored for custom engines: the remote server manages its own serving");
|
|
125885
|
+
}
|
|
125886
|
+
break;
|
|
125778
125887
|
case "exllamav3":
|
|
125779
125888
|
case "llama.cpp":
|
|
125780
125889
|
case "mlx-lm":
|
|
@@ -125830,7 +125939,9 @@ class ModelManager extends EventEmitter {
|
|
|
125830
125939
|
});
|
|
125831
125940
|
try {
|
|
125832
125941
|
this.engineProcess = await this.startEngineProcess();
|
|
125833
|
-
|
|
125942
|
+
if (this.engineProcess) {
|
|
125943
|
+
this.bindEngineProcessEvents(this.engineProcess);
|
|
125944
|
+
}
|
|
125834
125945
|
this.logger.info("Started LLM engine", {
|
|
125835
125946
|
agentEngineType: this.engine
|
|
125836
125947
|
});
|
|
@@ -125849,7 +125960,7 @@ class ModelManager extends EventEmitter {
|
|
|
125849
125960
|
if (!alreadyEmitted) {
|
|
125850
125961
|
this.emit("engineError", err);
|
|
125851
125962
|
}
|
|
125852
|
-
if (this.engineProcess) {
|
|
125963
|
+
if (this.engineProcess || this.engine === "custom") {
|
|
125853
125964
|
this.startHealthPoll();
|
|
125854
125965
|
}
|
|
125855
125966
|
throw err;
|
|
@@ -125857,6 +125968,9 @@ class ModelManager extends EventEmitter {
|
|
|
125857
125968
|
this.lifecycleState = "running";
|
|
125858
125969
|
this.reachedRunningState = true;
|
|
125859
125970
|
this.emit("engineReady");
|
|
125971
|
+
if (this.engine === "custom") {
|
|
125972
|
+
this.startHealthPoll();
|
|
125973
|
+
}
|
|
125860
125974
|
}
|
|
125861
125975
|
async stop() {
|
|
125862
125976
|
if (this.lifecycleState === "stopping") {
|
|
@@ -125877,6 +125991,8 @@ class ModelManager extends EventEmitter {
|
|
|
125877
125991
|
this.clearHealthPoll();
|
|
125878
125992
|
const processManager = this.engineProcess;
|
|
125879
125993
|
if (!processManager) {
|
|
125994
|
+
this.stopRequested = true;
|
|
125995
|
+
this.reachedRunningState = false;
|
|
125880
125996
|
this.lifecycleState = "stopped";
|
|
125881
125997
|
return;
|
|
125882
125998
|
}
|
|
@@ -125902,11 +126018,25 @@ class ModelManager extends EventEmitter {
|
|
|
125902
126018
|
get state() {
|
|
125903
126019
|
return this.lifecycleState;
|
|
125904
126020
|
}
|
|
126021
|
+
get resolvedServedModelName() {
|
|
126022
|
+
if (this.engine !== "custom")
|
|
126023
|
+
return null;
|
|
126024
|
+
return this.discoveredModelNames[0] ?? null;
|
|
126025
|
+
}
|
|
125905
126026
|
get wasRunning() {
|
|
125906
126027
|
return this.reachedRunningState;
|
|
125907
126028
|
}
|
|
126029
|
+
get customBaseURL() {
|
|
126030
|
+
if (this.engine !== "custom")
|
|
126031
|
+
return null;
|
|
126032
|
+
const baseUrl = this.engineConfig?.baseUrl;
|
|
126033
|
+
return typeof baseUrl === "string" && baseUrl.length > 0 ? baseUrl : null;
|
|
126034
|
+
}
|
|
125908
126035
|
async checkEngineReadiness() {
|
|
125909
126036
|
switch (this.engine) {
|
|
126037
|
+
case "custom": {
|
|
126038
|
+
return this.checkCustomReadiness();
|
|
126039
|
+
}
|
|
125910
126040
|
case "llama.cpp": {
|
|
125911
126041
|
return this.checkLlamacppReadiness();
|
|
125912
126042
|
}
|
|
@@ -125923,6 +126053,51 @@ class ModelManager extends EventEmitter {
|
|
|
125923
126053
|
return "ready";
|
|
125924
126054
|
}
|
|
125925
126055
|
}
|
|
126056
|
+
async checkCustomReadiness() {
|
|
126057
|
+
const baseURL = this.customBaseURL;
|
|
126058
|
+
if (!baseURL) {
|
|
126059
|
+
return "unreachable";
|
|
126060
|
+
}
|
|
126061
|
+
try {
|
|
126062
|
+
const response = await undiciExports.fetch(joinURL(baseURL, "/v1/models"), {
|
|
126063
|
+
method: "GET",
|
|
126064
|
+
signal: AbortSignal.timeout(5000)
|
|
126065
|
+
});
|
|
126066
|
+
if (response.status === 503) {
|
|
126067
|
+
return "loading";
|
|
126068
|
+
}
|
|
126069
|
+
if (!response.ok) {
|
|
126070
|
+
return "unreachable";
|
|
126071
|
+
}
|
|
126072
|
+
const payload = (await response.json());
|
|
126073
|
+
const models = Array.isArray(payload.data) ? payload.data : [];
|
|
126074
|
+
const modelIDs = models
|
|
126075
|
+
.map(model => {
|
|
126076
|
+
if (model === null || typeof model !== "object")
|
|
126077
|
+
return null;
|
|
126078
|
+
const id = model.id;
|
|
126079
|
+
return typeof id === "string" ? id : null;
|
|
126080
|
+
})
|
|
126081
|
+
.filter((id) => id !== null);
|
|
126082
|
+
if (modelIDs.length === 0) {
|
|
126083
|
+
this.logger.warn("Custom engine endpoint exposed no models via /v1/models", {
|
|
126084
|
+
engineBaseURL: baseURL
|
|
126085
|
+
});
|
|
126086
|
+
return "loading";
|
|
126087
|
+
}
|
|
126088
|
+
if (modelIDs.join("\n") !== this.discoveredModelNames.join("\n")) {
|
|
126089
|
+
this.logger.info("Discovered models on custom engine endpoint", {
|
|
126090
|
+
engineBaseURL: baseURL,
|
|
126091
|
+
models: modelIDs
|
|
126092
|
+
});
|
|
126093
|
+
this.discoveredModelNames = modelIDs;
|
|
126094
|
+
}
|
|
126095
|
+
return "ready";
|
|
126096
|
+
}
|
|
126097
|
+
catch (_error) {
|
|
126098
|
+
return "unreachable";
|
|
126099
|
+
}
|
|
126100
|
+
}
|
|
125926
126101
|
async checkGenericHealthReadiness() {
|
|
125927
126102
|
try {
|
|
125928
126103
|
const response = await undiciExports.fetch(joinURL(`http://localhost:${this.enginePort}`, "/health"), {
|
|
@@ -125978,18 +126153,24 @@ class ModelManager extends EventEmitter {
|
|
|
125978
126153
|
}
|
|
125979
126154
|
}
|
|
125980
126155
|
async waitForEngineReady() {
|
|
125981
|
-
const maxWaitMs = 15 * 60 * 1000;
|
|
126156
|
+
const maxWaitMs = Number.parseInt(process.env.ENGINE_STARTUP_TIMEOUT_MS ?? "", 10) || 15 * 60 * 1000;
|
|
125982
126157
|
const pollIntervalMs = 2000;
|
|
125983
126158
|
const start = Date.now();
|
|
125984
126159
|
while (Date.now() - start < maxWaitMs) {
|
|
125985
|
-
if (this.lifecycleState === "stopping") {
|
|
126160
|
+
if (this.lifecycleState === "stopping" || this.stopRequested) {
|
|
125986
126161
|
throw new Error("LLM engine startup interrupted by stop request");
|
|
125987
126162
|
}
|
|
125988
|
-
if (!this.engineProcess) {
|
|
126163
|
+
if (!this.engineProcess && this.engine !== "custom") {
|
|
125989
126164
|
throw new Error("LLM engine process exited before readiness checks completed");
|
|
125990
126165
|
}
|
|
125991
126166
|
const readiness = await this.checkEngineReadiness();
|
|
125992
126167
|
if (readiness === "ready") {
|
|
126168
|
+
// A stop() may have landed while the readiness request was
|
|
126169
|
+
// in flight; re-check before declaring ready.
|
|
126170
|
+
const lifecycleState = this.lifecycleState;
|
|
126171
|
+
if (lifecycleState === "stopping" || this.stopRequested) {
|
|
126172
|
+
throw new Error("LLM engine startup interrupted by stop request");
|
|
126173
|
+
}
|
|
125993
126174
|
return;
|
|
125994
126175
|
}
|
|
125995
126176
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
@@ -126007,19 +126188,32 @@ class ModelManager extends EventEmitter {
|
|
|
126007
126188
|
}
|
|
126008
126189
|
startHealthPoll() {
|
|
126009
126190
|
this.clearHealthPoll();
|
|
126010
|
-
this.logger.info("Starting background health poll
|
|
126191
|
+
this.logger.info("Starting background engine health poll", {
|
|
126192
|
+
agentEngineType: this.engine
|
|
126193
|
+
});
|
|
126011
126194
|
this.healthPollInterval = setInterval(() => {
|
|
126012
|
-
if (!this.engineProcess) {
|
|
126195
|
+
if (!this.engineProcess && this.engine !== "custom") {
|
|
126013
126196
|
this.clearHealthPoll();
|
|
126014
126197
|
return;
|
|
126015
126198
|
}
|
|
126016
126199
|
this.checkEngineReadiness()
|
|
126017
126200
|
.then(readiness => {
|
|
126018
126201
|
if (readiness === "ready") {
|
|
126019
|
-
this.
|
|
126020
|
-
|
|
126021
|
-
|
|
126022
|
-
|
|
126202
|
+
if (this.lifecycleState === "errored" ||
|
|
126203
|
+
this.lifecycleState === "starting") {
|
|
126204
|
+
this.lifecycleState = "running";
|
|
126205
|
+
this.reachedRunningState = true;
|
|
126206
|
+
this.emit("engineReady");
|
|
126207
|
+
}
|
|
126208
|
+
if (this.engine !== "custom") {
|
|
126209
|
+
this.clearHealthPoll();
|
|
126210
|
+
}
|
|
126211
|
+
return;
|
|
126212
|
+
}
|
|
126213
|
+
if (this.engine === "custom" &&
|
|
126214
|
+
readiness === "unreachable" &&
|
|
126215
|
+
this.lifecycleState === "running") {
|
|
126216
|
+
this.recordEngineError(new Error(`Custom engine endpoint unreachable: ${this.customBaseURL}`));
|
|
126023
126217
|
}
|
|
126024
126218
|
})
|
|
126025
126219
|
.catch(() => {
|
|
@@ -126044,6 +126238,15 @@ class ModelManager extends EventEmitter {
|
|
|
126044
126238
|
this.lastEngineError = err;
|
|
126045
126239
|
this.emit("engineError", err);
|
|
126046
126240
|
}
|
|
126241
|
+
requireCustomBaseURL() {
|
|
126242
|
+
const baseURL = this.customBaseURL;
|
|
126243
|
+
if (!baseURL) {
|
|
126244
|
+
throw new ConfigurationInvalidError({
|
|
126245
|
+
message: "Custom engine requires a base URL"
|
|
126246
|
+
});
|
|
126247
|
+
}
|
|
126248
|
+
return baseURL;
|
|
126249
|
+
}
|
|
126047
126250
|
async releaseDownloadLock() {
|
|
126048
126251
|
const handle = this.downloadLockHandle;
|
|
126049
126252
|
if (!handle)
|
|
@@ -126110,6 +126313,8 @@ class ModelManager extends EventEmitter {
|
|
|
126110
126313
|
async startEngineProcess() {
|
|
126111
126314
|
const targetDir = path$1.join(this.modelsDirectory, this.uniqueName);
|
|
126112
126315
|
switch (this.engine) {
|
|
126316
|
+
case "custom":
|
|
126317
|
+
return null;
|
|
126113
126318
|
case "exllamav3":
|
|
126114
126319
|
return startExllamav3.call(this, {
|
|
126115
126320
|
enginePort: this.enginePort,
|
|
@@ -126421,8 +126626,9 @@ function isEngineUsageChunk(value) {
|
|
|
126421
126626
|
}
|
|
126422
126627
|
return true;
|
|
126423
126628
|
}
|
|
126424
|
-
function monitorEngineResponseStream({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt }) {
|
|
126629
|
+
function monitorEngineResponseStream({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt, responseModelName }) {
|
|
126425
126630
|
const startedAt = requestStartedAt ?? Date.now();
|
|
126631
|
+
const clientModelName = responseModelName ?? null;
|
|
126426
126632
|
const passThrough = new require$$0$8.PassThrough();
|
|
126427
126633
|
passThrough.on("error", (error) => {
|
|
126428
126634
|
logger.error("Engine response stream error", {
|
|
@@ -126434,53 +126640,61 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126434
126640
|
let firstChunkAt = null;
|
|
126435
126641
|
let usage = null;
|
|
126436
126642
|
let buffer = "";
|
|
126643
|
+
let pendingFragment = "";
|
|
126437
126644
|
let completed = false;
|
|
126438
|
-
function
|
|
126439
|
-
const
|
|
126440
|
-
|
|
126441
|
-
|
|
126442
|
-
|
|
126443
|
-
|
|
126444
|
-
|
|
126445
|
-
|
|
126446
|
-
|
|
126645
|
+
function rewriteDataLine(rawLine) {
|
|
126646
|
+
const line = rawLine.trim();
|
|
126647
|
+
if (!line.startsWith("data:")) {
|
|
126648
|
+
return rawLine;
|
|
126649
|
+
}
|
|
126650
|
+
const payload = line.slice(5).trim();
|
|
126651
|
+
if (!payload || payload === "[DONE]") {
|
|
126652
|
+
return rawLine;
|
|
126653
|
+
}
|
|
126654
|
+
try {
|
|
126655
|
+
const parsed = JSON.parse(payload);
|
|
126656
|
+
let modified = false;
|
|
126657
|
+
if (coerceToolCallArguments(parsed)) {
|
|
126658
|
+
modified = true;
|
|
126447
126659
|
}
|
|
126448
|
-
|
|
126449
|
-
|
|
126450
|
-
|
|
126451
|
-
|
|
126660
|
+
if (clientModelName !== null &&
|
|
126661
|
+
typeof parsed.model === "string" &&
|
|
126662
|
+
parsed.model !== clientModelName) {
|
|
126663
|
+
parsed.model = clientModelName;
|
|
126664
|
+
modified = true;
|
|
126452
126665
|
}
|
|
126453
|
-
|
|
126454
|
-
const
|
|
126455
|
-
|
|
126456
|
-
|
|
126666
|
+
if (parsed.usage) {
|
|
126667
|
+
const usageChunk = parsed.usage;
|
|
126668
|
+
const effectiveContext = getEffectiveContextLength({
|
|
126669
|
+
contextLength,
|
|
126670
|
+
engineConfig,
|
|
126671
|
+
engineType
|
|
126672
|
+
});
|
|
126673
|
+
if (usageChunk.context_usage === undefined &&
|
|
126674
|
+
usageChunk.prompt_tokens !== undefined &&
|
|
126675
|
+
effectiveContext !== null) {
|
|
126676
|
+
usageChunk.context_usage = usageChunk.prompt_tokens / effectiveContext;
|
|
126457
126677
|
modified = true;
|
|
126458
126678
|
}
|
|
126459
|
-
if (parsed.usage) {
|
|
126460
|
-
const usageChunk = parsed.usage;
|
|
126461
|
-
const effectiveContext = getEffectiveContextLength({
|
|
126462
|
-
contextLength,
|
|
126463
|
-
engineConfig,
|
|
126464
|
-
engineType
|
|
126465
|
-
});
|
|
126466
|
-
if (usageChunk.context_usage === undefined &&
|
|
126467
|
-
usageChunk.prompt_tokens !== undefined &&
|
|
126468
|
-
effectiveContext !== null) {
|
|
126469
|
-
usageChunk.context_usage = usageChunk.prompt_tokens / effectiveContext;
|
|
126470
|
-
modified = true;
|
|
126471
|
-
}
|
|
126472
|
-
}
|
|
126473
|
-
if (modified) {
|
|
126474
|
-
modifiedLines.push("data: " + JSON.stringify(parsed));
|
|
126475
|
-
continue;
|
|
126476
|
-
}
|
|
126477
126679
|
}
|
|
126478
|
-
|
|
126479
|
-
|
|
126680
|
+
if (modified) {
|
|
126681
|
+
return "data: " + JSON.stringify(parsed);
|
|
126480
126682
|
}
|
|
126481
|
-
modifiedLines.push(rawLine);
|
|
126482
126683
|
}
|
|
126483
|
-
|
|
126684
|
+
catch (_error) {
|
|
126685
|
+
// Ignore malformed chunks
|
|
126686
|
+
}
|
|
126687
|
+
return rawLine;
|
|
126688
|
+
}
|
|
126689
|
+
// SSE events can split across transport chunks: hold back the trailing
|
|
126690
|
+
// (newline-less) fragment and only rewrite complete data lines, so a
|
|
126691
|
+
// partial JSON event is never forwarded with its upstream model name.
|
|
126692
|
+
function modifyChunkWithUsage(chunk, flush = false) {
|
|
126693
|
+
const combined = pendingFragment + chunk.toString("utf8");
|
|
126694
|
+
const lines = combined.split("\n");
|
|
126695
|
+
pendingFragment = flush ? "" : (lines.pop() ?? "");
|
|
126696
|
+
const modifiedLines = lines.map(rewriteDataLine);
|
|
126697
|
+
return Buffer.from(modifiedLines.length > 0 ? modifiedLines.join("\n") + (flush ? "" : "\n") : "", "utf8");
|
|
126484
126698
|
}
|
|
126485
126699
|
function parseUsageFromBuffer() {
|
|
126486
126700
|
const lines = buffer.split("\n");
|
|
@@ -126576,6 +126790,9 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126576
126790
|
if (buffer.length > 0) {
|
|
126577
126791
|
parseUsageFromBuffer();
|
|
126578
126792
|
}
|
|
126793
|
+
if (pendingFragment.length > 0) {
|
|
126794
|
+
passThrough.write(modifyChunkWithUsage(Buffer.from("", "utf8"), true));
|
|
126795
|
+
}
|
|
126579
126796
|
logEngineMetrics({
|
|
126580
126797
|
agentEngineType,
|
|
126581
126798
|
level: "info",
|
|
@@ -126618,9 +126835,11 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126618
126835
|
stream: passThrough
|
|
126619
126836
|
};
|
|
126620
126837
|
}
|
|
126621
|
-
function monitorEngineResponseSingle({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt }) {
|
|
126838
|
+
function monitorEngineResponseSingle({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt, responseModelName }) {
|
|
126622
126839
|
const maxUsageCaptureBytes = 1024 * 1024;
|
|
126623
126840
|
const startedAt = requestStartedAt ?? Date.now();
|
|
126841
|
+
const clientModelName = responseModelName ?? null;
|
|
126842
|
+
const rewriteBuffer = clientModelName !== null ? [] : null;
|
|
126624
126843
|
const passThrough = new require$$0$8.PassThrough();
|
|
126625
126844
|
passThrough.on("error", (error) => {
|
|
126626
126845
|
logger.error("Engine response stream error", {
|
|
@@ -126676,7 +126895,12 @@ function monitorEngineResponseSingle({ agentEngineType, body, contextLength, eng
|
|
|
126676
126895
|
usageChunks.length = 0;
|
|
126677
126896
|
}
|
|
126678
126897
|
}
|
|
126679
|
-
|
|
126898
|
+
if (rewriteBuffer) {
|
|
126899
|
+
rewriteBuffer.push(chunkBuffer);
|
|
126900
|
+
}
|
|
126901
|
+
else {
|
|
126902
|
+
passThrough.write(chunkBuffer);
|
|
126903
|
+
}
|
|
126680
126904
|
});
|
|
126681
126905
|
body.once("error", err => {
|
|
126682
126906
|
logEngineMetrics({
|
|
@@ -126736,6 +126960,24 @@ function monitorEngineResponseSingle({ agentEngineType, body, contextLength, eng
|
|
|
126736
126960
|
responseBytes,
|
|
126737
126961
|
usage
|
|
126738
126962
|
});
|
|
126963
|
+
if (rewriteBuffer) {
|
|
126964
|
+
const original = Buffer.concat(rewriteBuffer);
|
|
126965
|
+
let output = original;
|
|
126966
|
+
try {
|
|
126967
|
+
const parsed = JSON.parse(original.toString("utf8"));
|
|
126968
|
+
if (parsed !== null &&
|
|
126969
|
+
typeof parsed === "object" &&
|
|
126970
|
+
typeof parsed.model === "string" &&
|
|
126971
|
+
parsed.model !== clientModelName) {
|
|
126972
|
+
parsed.model = clientModelName;
|
|
126973
|
+
output = Buffer.from(JSON.stringify(parsed), "utf8");
|
|
126974
|
+
}
|
|
126975
|
+
}
|
|
126976
|
+
catch (_error) {
|
|
126977
|
+
// Non-JSON body: pass through untouched
|
|
126978
|
+
}
|
|
126979
|
+
passThrough.write(output);
|
|
126980
|
+
}
|
|
126739
126981
|
finalize(null);
|
|
126740
126982
|
passThrough.end();
|
|
126741
126983
|
});
|
|
@@ -126872,7 +127114,7 @@ function applyChatTemplateKwargs({ body, model }) {
|
|
|
126872
127114
|
}
|
|
126873
127115
|
return payload;
|
|
126874
127116
|
}
|
|
126875
|
-
function serializeRequestBody$1(body, { model, path } = {}) {
|
|
127117
|
+
function serializeRequestBody$1(body, { model, path, servedModelName } = {}) {
|
|
126876
127118
|
if (!isPlainObject$a(body)) {
|
|
126877
127119
|
const payload = typeof body === "string" ? body : JSON.stringify(body);
|
|
126878
127120
|
return {
|
|
@@ -126881,6 +127123,9 @@ function serializeRequestBody$1(body, { model, path } = {}) {
|
|
|
126881
127123
|
};
|
|
126882
127124
|
}
|
|
126883
127125
|
let requestPayload = { ...body };
|
|
127126
|
+
if (servedModelName) {
|
|
127127
|
+
requestPayload.model = servedModelName;
|
|
127128
|
+
}
|
|
126884
127129
|
if (path === "/v1/chat/completions" && model) {
|
|
126885
127130
|
requestPayload = applyChatTemplateKwargs({ body: requestPayload, model });
|
|
126886
127131
|
}
|
|
@@ -126937,8 +127182,15 @@ async function proxyEmbeddingsRoute({ body, conduitConfiguration, endpointId, lo
|
|
|
126937
127182
|
}
|
|
126938
127183
|
const engineType = conduitConfiguration.engineConfig?.type ?? null;
|
|
126939
127184
|
const engineConfig = conduitConfiguration.engineConfig ?? null;
|
|
127185
|
+
const servedModelName = modelManager.resolvedServedModelName;
|
|
127186
|
+
const clientModelName = isPlainObject$a(body) && typeof body.model === "string" ? body.model : null;
|
|
126940
127187
|
const serializedBody = isPlainObject$a(body)
|
|
126941
|
-
? JSON.stringify(
|
|
127188
|
+
? JSON.stringify(servedModelName
|
|
127189
|
+
? {
|
|
127190
|
+
...body,
|
|
127191
|
+
model: servedModelName
|
|
127192
|
+
}
|
|
127193
|
+
: body)
|
|
126942
127194
|
: typeof body === "string"
|
|
126943
127195
|
? body
|
|
126944
127196
|
: JSON.stringify(body);
|
|
@@ -127056,7 +127308,8 @@ async function proxyEmbeddingsRoute({ body, conduitConfiguration, endpointId, lo
|
|
|
127056
127308
|
onComplete: onMonitoringComplete,
|
|
127057
127309
|
requestBodyBytes,
|
|
127058
127310
|
requestPath: "/v1/embeddings",
|
|
127059
|
-
requestStartedAt
|
|
127311
|
+
requestStartedAt,
|
|
127312
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127060
127313
|
});
|
|
127061
127314
|
return {
|
|
127062
127315
|
body: monitoredResponse.stream,
|
|
@@ -127081,8 +127334,10 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127081
127334
|
}
|
|
127082
127335
|
const engineType = conduitConfiguration.engineConfig?.type ?? null;
|
|
127083
127336
|
const engineConfig = conduitConfiguration.engineConfig ?? null;
|
|
127337
|
+
const servedModelName = modelManager.resolvedServedModelName;
|
|
127338
|
+
const clientModelName = isPlainObject$a(body) && typeof body.model === "string" ? body.model : null;
|
|
127084
127339
|
const effectiveBody = modelManager.model.multimodalEnabled ? body : stripImagesFromBody(body);
|
|
127085
|
-
const { bytes: requestBodyBytes, payload: serializedBody } = serializeRequestBody$1(effectiveBody, { model: modelManager.model, path });
|
|
127340
|
+
const { bytes: requestBodyBytes, payload: serializedBody } = serializeRequestBody$1(effectiveBody, { model: modelManager.model, path, servedModelName });
|
|
127086
127341
|
const requestStartedAt = Date.now();
|
|
127087
127342
|
const requestBody = JSON.parse(serializedBody);
|
|
127088
127343
|
const streamRequested = requestBody.stream === true;
|
|
@@ -127222,7 +127477,8 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127222
127477
|
onComplete: onMonitoringComplete,
|
|
127223
127478
|
requestBodyBytes,
|
|
127224
127479
|
requestPath: path,
|
|
127225
|
-
requestStartedAt
|
|
127480
|
+
requestStartedAt,
|
|
127481
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127226
127482
|
})
|
|
127227
127483
|
: monitorEngineResponseSingle({
|
|
127228
127484
|
agentEngineType: engineType ?? "unknown",
|
|
@@ -127234,7 +127490,8 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127234
127490
|
onComplete: onMonitoringComplete,
|
|
127235
127491
|
requestBodyBytes,
|
|
127236
127492
|
requestPath: path,
|
|
127237
|
-
requestStartedAt
|
|
127493
|
+
requestStartedAt,
|
|
127494
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127238
127495
|
});
|
|
127239
127496
|
return {
|
|
127240
127497
|
body: monitoredResponse.stream,
|
|
@@ -158743,13 +159000,16 @@ async function detectDockerVersion() {
|
|
|
158743
159000
|
* Flattens flat CLI extra-arg tokens into [arg, value] pairs, sorted by ARG NAME (ascending, ties by
|
|
158744
159001
|
* value). `--flag=value` pairs split on the first `=`; a bare `--flag` consumes the following token
|
|
158745
159002
|
* as its value when that token does not start with "-" (classic CLI convention); anything else
|
|
158746
|
-
* (flags, non-strings) is dropped.
|
|
159003
|
+
* (flags, non-strings) is dropped. Secret-like option values are masked before pairing so they
|
|
159004
|
+
* never reach execution reports.
|
|
158747
159005
|
*/
|
|
158748
159006
|
function pairExtraArgs(tokens) {
|
|
158749
159007
|
if (!Array.isArray(tokens)) {
|
|
158750
159008
|
return [];
|
|
158751
159009
|
}
|
|
158752
|
-
|
|
159010
|
+
// Non-string tokens become empty strings: they keep their position as a
|
|
159011
|
+
// non-consumable barrier while allowing secret masking over string tokens.
|
|
159012
|
+
const list = redactSecretArgs(tokens.map(token => (typeof token === "string" ? token : "")));
|
|
158753
159013
|
const pairs = [];
|
|
158754
159014
|
let index = 0;
|
|
158755
159015
|
while (index < list.length) {
|
|
@@ -158885,9 +159145,9 @@ async function createApplication({ abortController, apiClient, configuration, lo
|
|
|
158885
159145
|
}
|
|
158886
159146
|
const reporter = new EngineExecutionReporter({
|
|
158887
159147
|
buildContext: () => {
|
|
158888
|
-
const engineType =
|
|
158889
|
-
"llama.cpp");
|
|
159148
|
+
const engineType = conduitConfiguration.engineConfig?.type ?? "llama.cpp";
|
|
158890
159149
|
const versions = {
|
|
159150
|
+
custom: null,
|
|
158891
159151
|
exllamav3: machine?.exllamav3Version ?? null,
|
|
158892
159152
|
"llama.cpp": machine?.llamaCppVersion ?? null,
|
|
158893
159153
|
"mlx-lm": machine?.mlxlmVersion ?? null,
|
|
@@ -159374,7 +159634,11 @@ function createModelManagerFromConfig(conduitConfiguration, configuration, logge
|
|
|
159374
159634
|
return new ModelManager({
|
|
159375
159635
|
contextLength: conduitConfiguration.contextLength ?? null,
|
|
159376
159636
|
engineConfig: engineConfig
|
|
159377
|
-
? {
|
|
159637
|
+
? {
|
|
159638
|
+
baseUrl: engineConfig.baseUrl ?? null,
|
|
159639
|
+
extraArgs: engineConfig.extraArgs,
|
|
159640
|
+
type: engineConfig.type
|
|
159641
|
+
}
|
|
159378
159642
|
: null,
|
|
159379
159643
|
enginePort: configuration.enginePort,
|
|
159380
159644
|
engineType: engineConfig?.type ?? "llama.cpp",
|
|
@@ -159385,7 +159649,11 @@ function createModelManagerFromConfig(conduitConfiguration, configuration, logge
|
|
|
159385
159649
|
}
|
|
159386
159650
|
function getConduitModelFileName(configuration) {
|
|
159387
159651
|
const { source } = configuration.targetModel;
|
|
159388
|
-
|
|
159652
|
+
if (source.type === "huggingface")
|
|
159653
|
+
return source.slug;
|
|
159654
|
+
if (source.type === "storage")
|
|
159655
|
+
return source.irid;
|
|
159656
|
+
return configuration.targetModel.id;
|
|
159389
159657
|
}
|
|
159390
159658
|
function getConduitModelName(configuration) {
|
|
159391
159659
|
return configuration.targetModel.id;
|