@infersec/conduit 1.112.1 → 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/apiClient/index.d.ts +2 -1
- package/dist/cli.js +713 -85
- package/dist/cli.sea.cjs +713 -85
- package/dist/commands/engineOptions.d.ts +1 -0
- package/dist/modelManagement/ModelManager.d.ts +11 -0
- package/dist/modelManagement/classifyEngineError.d.ts +12 -0
- package/dist/reporting/engineExecutionReporter.d.ts +55 -0
- package/dist/reporting/index.d.ts +1 -0
- package/dist/requestHandlers/createConduitAnthropicAPIReferenceHandlers.d.ts +1 -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,
|
|
@@ -19972,6 +19994,32 @@ object$5({
|
|
|
19972
19994
|
sizeBytes: number$1().int().nonnegative().nullable()
|
|
19973
19995
|
});
|
|
19974
19996
|
|
|
19997
|
+
const EngineExecutionErrorTypeSchema = _enum$1([
|
|
19998
|
+
"config",
|
|
19999
|
+
"crash",
|
|
20000
|
+
"oom",
|
|
20001
|
+
"prompt",
|
|
20002
|
+
"unknown"
|
|
20003
|
+
]);
|
|
20004
|
+
const EngineExecutionReportPayloadSchema = object$5({
|
|
20005
|
+
avgTps: number$1().nonnegative().finite().default(0),
|
|
20006
|
+
completionTokens: number$1().int().nonnegative().default(0),
|
|
20007
|
+
durationMs: number$1().int().nonnegative().default(0),
|
|
20008
|
+
engineType: LLMEngineSchema.nullable(),
|
|
20009
|
+
engineVersion: string$2().max(64).nullable().default(null),
|
|
20010
|
+
errorDetail: string$2().max(2048).nullable().default(null),
|
|
20011
|
+
errorType: EngineExecutionErrorTypeSchema.nullable(),
|
|
20012
|
+
extraArgs: array$1(tuple([string$2().min(1).max(128), string$2().max(512)]))
|
|
20013
|
+
.max(256)
|
|
20014
|
+
.default([]),
|
|
20015
|
+
finishedAtISO: string$2().datetime({ offset: true }),
|
|
20016
|
+
peakTps: number$1().nonnegative().finite().nullable().default(null),
|
|
20017
|
+
promptTokens: number$1().int().nonnegative().default(0),
|
|
20018
|
+
runAtISO: string$2().datetime({ offset: true }),
|
|
20019
|
+
success: boolean$1(),
|
|
20020
|
+
ttftMs: number$1().int().nonnegative().default(0),
|
|
20021
|
+
totalTokens: number$1().int().nonnegative().default(0)
|
|
20022
|
+
});
|
|
19975
20023
|
const InferenceAgentLLMMetricsPayloadSchema = object$5({
|
|
19976
20024
|
bytes: number$1().int().nonnegative(),
|
|
19977
20025
|
completionTokens: number$1().int().nonnegative(),
|
|
@@ -20310,6 +20358,23 @@ const API_SERVICE_CONDUIT_API_REFERENCE = {
|
|
|
20310
20358
|
}
|
|
20311
20359
|
}
|
|
20312
20360
|
},
|
|
20361
|
+
"/conduit/api/v1/source/:sourceID/engine/execution": {
|
|
20362
|
+
POST: {
|
|
20363
|
+
auth: {
|
|
20364
|
+
type: "api-key"
|
|
20365
|
+
},
|
|
20366
|
+
body: EngineExecutionReportPayloadSchema,
|
|
20367
|
+
parameters: {
|
|
20368
|
+
sourceID: ULIDSchema
|
|
20369
|
+
},
|
|
20370
|
+
response: {
|
|
20371
|
+
schema: object$5({
|
|
20372
|
+
acknowledged: literal(true)
|
|
20373
|
+
}),
|
|
20374
|
+
type: "rest"
|
|
20375
|
+
}
|
|
20376
|
+
}
|
|
20377
|
+
},
|
|
20313
20378
|
"/conduit/api/v1/source/:sourceID/requests/:requestID/chunk": {
|
|
20314
20379
|
POST: {
|
|
20315
20380
|
auth: {
|
|
@@ -21062,7 +21127,8 @@ const CreateModelResponseSchema = object$5({
|
|
|
21062
21127
|
const CreateSourceBodySchema = object$5({
|
|
21063
21128
|
contextLength: number$1().int().positive().max(1048576).optional(),
|
|
21064
21129
|
engineId: ULIDSchema,
|
|
21065
|
-
|
|
21130
|
+
// Optional for custom engines, which serve externally managed models
|
|
21131
|
+
modelID: ULIDSchema.nullable().optional(),
|
|
21066
21132
|
name: ResourceNameSchema,
|
|
21067
21133
|
quantizationLabel: string$2().min(1).max(128).optional()
|
|
21068
21134
|
});
|
|
@@ -21111,7 +21177,7 @@ const SourceDetailResponseSchema = object$5({
|
|
|
21111
21177
|
const UpdateSourceBodySchema = object$5({
|
|
21112
21178
|
contextLength: number$1().int().positive().nullable().optional(),
|
|
21113
21179
|
engineId: ULIDSchema.nullable().optional(),
|
|
21114
|
-
modelID: ULIDSchema.optional(),
|
|
21180
|
+
modelID: ULIDSchema.nullable().optional(),
|
|
21115
21181
|
name: ResourceNameSchema.optional(),
|
|
21116
21182
|
quantizationLabel: string$2().min(1).max(128).nullable().optional()
|
|
21117
21183
|
});
|
|
@@ -21235,6 +21301,7 @@ const CreateEndpointResponseSchema = object$5({
|
|
|
21235
21301
|
id: ULIDSchema
|
|
21236
21302
|
});
|
|
21237
21303
|
const EngineOutputSchema = object$5({
|
|
21304
|
+
baseUrl: string$2().nullable(),
|
|
21238
21305
|
created: string$2(),
|
|
21239
21306
|
extraArgs: array$1(string$2()),
|
|
21240
21307
|
id: ULIDSchema,
|
|
@@ -21243,11 +21310,29 @@ const EngineOutputSchema = object$5({
|
|
|
21243
21310
|
updated: string$2()
|
|
21244
21311
|
});
|
|
21245
21312
|
const CreateEngineBodySchema = object$5({
|
|
21313
|
+
baseUrl: string$2().url().nullable().optional(),
|
|
21246
21314
|
extraArgs: array$1(string$2()).optional(),
|
|
21247
21315
|
name: ResourceNameSchema,
|
|
21248
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
|
+
}
|
|
21249
21333
|
});
|
|
21250
21334
|
const UpdateEngineBodySchema = object$5({
|
|
21335
|
+
baseUrl: string$2().url().nullable().optional(),
|
|
21251
21336
|
extraArgs: array$1(string$2()).optional(),
|
|
21252
21337
|
name: ResourceNameSchema.optional(),
|
|
21253
21338
|
type: LLMEngineSchema.optional()
|
|
@@ -22053,6 +22138,11 @@ const RecommendedModelSchema = object$5({
|
|
|
22053
22138
|
const recommendedModels = RecommendedModelSchema.array().parse(modelsData);
|
|
22054
22139
|
|
|
22055
22140
|
const ENGINE_API_COMPATIBILITY = {
|
|
22141
|
+
custom: {
|
|
22142
|
+
nativeAnthropicMessages: false,
|
|
22143
|
+
supportsEmbeddings: true,
|
|
22144
|
+
supportsVision: true
|
|
22145
|
+
},
|
|
22056
22146
|
exllamav3: {
|
|
22057
22147
|
nativeAnthropicMessages: false,
|
|
22058
22148
|
supportsEmbeddings: false,
|
|
@@ -111702,6 +111792,27 @@ function registerEndpointCommands({ program }) {
|
|
|
111702
111792
|
}
|
|
111703
111793
|
|
|
111704
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
|
+
}
|
|
111705
111816
|
function buildEngineCreateBody(options) {
|
|
111706
111817
|
if (!options.name) {
|
|
111707
111818
|
throw new Error("--name is required");
|
|
@@ -111712,7 +111823,12 @@ function buildEngineCreateBody(options) {
|
|
|
111712
111823
|
if (!ENGINE_TYPES.includes(options.type)) {
|
|
111713
111824
|
throw new Error(`Invalid engine type: ${options.type} (expected one of: ${ENGINE_TYPES.join(", ")})`);
|
|
111714
111825
|
}
|
|
111826
|
+
const baseUrl = validateEngineBaseURL({
|
|
111827
|
+
baseUrl: options.baseUrl,
|
|
111828
|
+
type: options.type
|
|
111829
|
+
});
|
|
111715
111830
|
return {
|
|
111831
|
+
baseUrl,
|
|
111716
111832
|
extraArgs: options.arg ?? [],
|
|
111717
111833
|
name: options.name,
|
|
111718
111834
|
type: options.type
|
|
@@ -111730,6 +111846,24 @@ function buildEngineUpdateBody(options) {
|
|
|
111730
111846
|
}
|
|
111731
111847
|
if (options.arg !== undefined)
|
|
111732
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
|
+
}
|
|
111733
111867
|
return body;
|
|
111734
111868
|
}
|
|
111735
111869
|
|
|
@@ -111743,10 +111877,11 @@ function registerEngineCommands({ program }) {
|
|
|
111743
111877
|
.description("Create or update an inference engine resource")
|
|
111744
111878
|
.option("--api-url <url>", "API base URL (required, no environment variable fallback)")
|
|
111745
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)")
|
|
111746
111881
|
.option("--id <ulid>", "Target an existing engine by ID (requires --update)")
|
|
111747
111882
|
.option("--key <value>", "API key (required, no environment variable fallback)")
|
|
111748
111883
|
.option("--name <name>", "Engine name (matched by --update)")
|
|
111749
|
-
.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")
|
|
111750
111885
|
.option("--update", "Update an existing engine matched by name (or --id) instead of erroring")
|
|
111751
111886
|
.action(async (options) => {
|
|
111752
111887
|
const { apiURL, apiKey } = resolveManagementConnection(options);
|
|
@@ -113767,6 +113902,19 @@ function createAPIClient({ apiKey, apiURL, inferenceSourceID, logger }) {
|
|
|
113767
113902
|
route: "/conduit/api/v1/source/:sourceID/state"
|
|
113768
113903
|
});
|
|
113769
113904
|
},
|
|
113905
|
+
reportEngineExecution: async (payload) => {
|
|
113906
|
+
await fetchByReference({
|
|
113907
|
+
baseURL: apiURL,
|
|
113908
|
+
body: payload,
|
|
113909
|
+
fetch: fetchWithAPIKey,
|
|
113910
|
+
method: "POST",
|
|
113911
|
+
parameters: {
|
|
113912
|
+
sourceID: inferenceSourceID
|
|
113913
|
+
},
|
|
113914
|
+
reference: API_SERVICE_CONDUIT_API_REFERENCE,
|
|
113915
|
+
route: "/conduit/api/v1/source/:sourceID/engine/execution"
|
|
113916
|
+
});
|
|
113917
|
+
},
|
|
113770
113918
|
reportPromptMetrics: async (payload) => {
|
|
113771
113919
|
await fetchByReference({
|
|
113772
113920
|
baseURL: apiURL,
|
|
@@ -117733,6 +117881,9 @@ async function getChatTemplateEngineArgs({ engine, model, targetDirectory }) {
|
|
|
117733
117881
|
return [];
|
|
117734
117882
|
const flag = FLAG_BASED_ENGINE_ARGS[engine];
|
|
117735
117883
|
if (!flag) {
|
|
117884
|
+
if (engine === "custom") {
|
|
117885
|
+
console.warn("[chatTemplate] Custom engines manage their own serving; ignoring chat template override");
|
|
117886
|
+
}
|
|
117736
117887
|
if (engine === "tensorrt-llm") {
|
|
117737
117888
|
console.warn("[chatTemplate] TensorRT-LLM does not support chat template overrides; ignoring");
|
|
117738
117889
|
}
|
|
@@ -124630,7 +124781,7 @@ function matchesQuantizationVariant({ filePath, variant }) {
|
|
|
124630
124781
|
return segments.slice(0, -1).some(segment => matcher.test(segment));
|
|
124631
124782
|
}
|
|
124632
124783
|
async function findQuantizedModelTarget({ model, path }) {
|
|
124633
|
-
if (model.source.type
|
|
124784
|
+
if (model.source.type !== "huggingface") {
|
|
124634
124785
|
throw new Error("Model storage not supported yet");
|
|
124635
124786
|
}
|
|
124636
124787
|
if (model.format !== "gguf") {
|
|
@@ -125545,7 +125696,11 @@ function sanitizeSegment(value) {
|
|
|
125545
125696
|
.replace(new RegExp(`${SEPARATOR}{2,}`, "g"), SEPARATOR);
|
|
125546
125697
|
}
|
|
125547
125698
|
function createModelStorageKey(model) {
|
|
125548
|
-
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;
|
|
125549
125704
|
return `${model.source.type}${SEPARATOR}${sanitizeSegment(identifier)}`;
|
|
125550
125705
|
}
|
|
125551
125706
|
|
|
@@ -125621,12 +125776,16 @@ class ModelManager extends EventEmitter {
|
|
|
125621
125776
|
uniqueName;
|
|
125622
125777
|
contextLength;
|
|
125623
125778
|
logger;
|
|
125779
|
+
discoveredModelNames = [];
|
|
125624
125780
|
engineProcess = null;
|
|
125625
125781
|
healthPollInterval = null;
|
|
125626
125782
|
lastEngineError = null;
|
|
125627
125783
|
lifecycleState = "stopped";
|
|
125628
125784
|
downloadLockHandle = null;
|
|
125629
125785
|
stopRequested = false;
|
|
125786
|
+
lastEngineExitCode = null;
|
|
125787
|
+
lastEngineExitSignal = null;
|
|
125788
|
+
reachedRunningState = false;
|
|
125630
125789
|
modelsDirectory;
|
|
125631
125790
|
constructor({ contextLength, engineConfig, enginePort, engineType, logger, model, root }) {
|
|
125632
125791
|
super();
|
|
@@ -125650,6 +125809,7 @@ class ModelManager extends EventEmitter {
|
|
|
125650
125809
|
}
|
|
125651
125810
|
async fetchOpenAI(path, opts) {
|
|
125652
125811
|
switch (this.engine) {
|
|
125812
|
+
case "custom":
|
|
125653
125813
|
case "exllamav3":
|
|
125654
125814
|
case "llama.cpp":
|
|
125655
125815
|
case "mlx-lm":
|
|
@@ -125657,6 +125817,9 @@ class ModelManager extends EventEmitter {
|
|
|
125657
125817
|
case "tensorrt-llm":
|
|
125658
125818
|
case "vllm": {
|
|
125659
125819
|
this.logger.debug(`Fetching from engine: ${path}`);
|
|
125820
|
+
const baseURL = this.engine === "custom"
|
|
125821
|
+
? this.requireCustomBaseURL()
|
|
125822
|
+
: `http://localhost:${this.enginePort}`;
|
|
125660
125823
|
const callerSignal = opts?.signal;
|
|
125661
125824
|
const controller = new AbortController();
|
|
125662
125825
|
const timeout = setTimeout(() => {
|
|
@@ -125667,7 +125830,7 @@ class ModelManager extends EventEmitter {
|
|
|
125667
125830
|
: controller.signal;
|
|
125668
125831
|
try {
|
|
125669
125832
|
const fetchStartedAt = Date.now();
|
|
125670
|
-
const response = await undiciExports.fetch(joinURL(
|
|
125833
|
+
const response = await undiciExports.fetch(joinURL(baseURL, path), {
|
|
125671
125834
|
...opts,
|
|
125672
125835
|
dispatcher: ENGINE_AGENT,
|
|
125673
125836
|
headers: {
|
|
@@ -125702,6 +125865,11 @@ class ModelManager extends EventEmitter {
|
|
|
125702
125865
|
modelID: this.model.id
|
|
125703
125866
|
});
|
|
125704
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;
|
|
125705
125873
|
case "exllamav3":
|
|
125706
125874
|
case "llama.cpp":
|
|
125707
125875
|
case "mlx-lm":
|
|
@@ -125749,12 +125917,17 @@ class ModelManager extends EventEmitter {
|
|
|
125749
125917
|
this.lifecycleState = "starting";
|
|
125750
125918
|
this.lastEngineError = null;
|
|
125751
125919
|
this.stopRequested = false;
|
|
125920
|
+
this.lastEngineExitCode = null;
|
|
125921
|
+
this.lastEngineExitSignal = null;
|
|
125922
|
+
this.reachedRunningState = false;
|
|
125752
125923
|
this.logger.info("Starting LLM engine", {
|
|
125753
125924
|
agentEngineType: this.engine
|
|
125754
125925
|
});
|
|
125755
125926
|
try {
|
|
125756
125927
|
this.engineProcess = await this.startEngineProcess();
|
|
125757
|
-
|
|
125928
|
+
if (this.engineProcess) {
|
|
125929
|
+
this.bindEngineProcessEvents(this.engineProcess);
|
|
125930
|
+
}
|
|
125758
125931
|
this.logger.info("Started LLM engine", {
|
|
125759
125932
|
agentEngineType: this.engine
|
|
125760
125933
|
});
|
|
@@ -125773,13 +125946,17 @@ class ModelManager extends EventEmitter {
|
|
|
125773
125946
|
if (!alreadyEmitted) {
|
|
125774
125947
|
this.emit("engineError", err);
|
|
125775
125948
|
}
|
|
125776
|
-
if (this.engineProcess) {
|
|
125949
|
+
if (this.engineProcess || this.engine === "custom") {
|
|
125777
125950
|
this.startHealthPoll();
|
|
125778
125951
|
}
|
|
125779
125952
|
throw err;
|
|
125780
125953
|
}
|
|
125781
125954
|
this.lifecycleState = "running";
|
|
125955
|
+
this.reachedRunningState = true;
|
|
125782
125956
|
this.emit("engineReady");
|
|
125957
|
+
if (this.engine === "custom") {
|
|
125958
|
+
this.startHealthPoll();
|
|
125959
|
+
}
|
|
125783
125960
|
}
|
|
125784
125961
|
async stop() {
|
|
125785
125962
|
if (this.lifecycleState === "stopping") {
|
|
@@ -125800,9 +125977,12 @@ class ModelManager extends EventEmitter {
|
|
|
125800
125977
|
this.clearHealthPoll();
|
|
125801
125978
|
const processManager = this.engineProcess;
|
|
125802
125979
|
if (!processManager) {
|
|
125980
|
+
this.stopRequested = true;
|
|
125981
|
+
this.reachedRunningState = false;
|
|
125803
125982
|
this.lifecycleState = "stopped";
|
|
125804
125983
|
return;
|
|
125805
125984
|
}
|
|
125985
|
+
this.reachedRunningState = false;
|
|
125806
125986
|
this.lifecycleState = "stopping";
|
|
125807
125987
|
this.stopRequested = true;
|
|
125808
125988
|
await processManager.stop();
|
|
@@ -125815,11 +125995,34 @@ class ModelManager extends EventEmitter {
|
|
|
125815
125995
|
this.lifecycleState === "starting" ||
|
|
125816
125996
|
this.lifecycleState === "errored");
|
|
125817
125997
|
}
|
|
125998
|
+
get lastExitCode() {
|
|
125999
|
+
return this.lastEngineExitCode;
|
|
126000
|
+
}
|
|
126001
|
+
get lastExitSignal() {
|
|
126002
|
+
return this.lastEngineExitSignal;
|
|
126003
|
+
}
|
|
125818
126004
|
get state() {
|
|
125819
126005
|
return this.lifecycleState;
|
|
125820
126006
|
}
|
|
126007
|
+
get resolvedServedModelName() {
|
|
126008
|
+
if (this.engine !== "custom")
|
|
126009
|
+
return null;
|
|
126010
|
+
return this.discoveredModelNames[0] ?? null;
|
|
126011
|
+
}
|
|
126012
|
+
get wasRunning() {
|
|
126013
|
+
return this.reachedRunningState;
|
|
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
|
+
}
|
|
125821
126021
|
async checkEngineReadiness() {
|
|
125822
126022
|
switch (this.engine) {
|
|
126023
|
+
case "custom": {
|
|
126024
|
+
return this.checkCustomReadiness();
|
|
126025
|
+
}
|
|
125823
126026
|
case "llama.cpp": {
|
|
125824
126027
|
return this.checkLlamacppReadiness();
|
|
125825
126028
|
}
|
|
@@ -125836,6 +126039,51 @@ class ModelManager extends EventEmitter {
|
|
|
125836
126039
|
return "ready";
|
|
125837
126040
|
}
|
|
125838
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
|
+
}
|
|
125839
126087
|
async checkGenericHealthReadiness() {
|
|
125840
126088
|
try {
|
|
125841
126089
|
const response = await undiciExports.fetch(joinURL(`http://localhost:${this.enginePort}`, "/health"), {
|
|
@@ -125891,18 +126139,24 @@ class ModelManager extends EventEmitter {
|
|
|
125891
126139
|
}
|
|
125892
126140
|
}
|
|
125893
126141
|
async waitForEngineReady() {
|
|
125894
|
-
const maxWaitMs = 15 * 60 * 1000;
|
|
126142
|
+
const maxWaitMs = Number.parseInt(process.env.ENGINE_STARTUP_TIMEOUT_MS ?? "", 10) || 15 * 60 * 1000;
|
|
125895
126143
|
const pollIntervalMs = 2000;
|
|
125896
126144
|
const start = Date.now();
|
|
125897
126145
|
while (Date.now() - start < maxWaitMs) {
|
|
125898
|
-
if (this.lifecycleState === "stopping") {
|
|
126146
|
+
if (this.lifecycleState === "stopping" || this.stopRequested) {
|
|
125899
126147
|
throw new Error("LLM engine startup interrupted by stop request");
|
|
125900
126148
|
}
|
|
125901
|
-
if (!this.engineProcess) {
|
|
126149
|
+
if (!this.engineProcess && this.engine !== "custom") {
|
|
125902
126150
|
throw new Error("LLM engine process exited before readiness checks completed");
|
|
125903
126151
|
}
|
|
125904
126152
|
const readiness = await this.checkEngineReadiness();
|
|
125905
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
|
+
}
|
|
125906
126160
|
return;
|
|
125907
126161
|
}
|
|
125908
126162
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
@@ -125920,18 +126174,32 @@ class ModelManager extends EventEmitter {
|
|
|
125920
126174
|
}
|
|
125921
126175
|
startHealthPoll() {
|
|
125922
126176
|
this.clearHealthPoll();
|
|
125923
|
-
this.logger.info("Starting background health poll
|
|
126177
|
+
this.logger.info("Starting background engine health poll", {
|
|
126178
|
+
agentEngineType: this.engine
|
|
126179
|
+
});
|
|
125924
126180
|
this.healthPollInterval = setInterval(() => {
|
|
125925
|
-
if (!this.engineProcess) {
|
|
126181
|
+
if (!this.engineProcess && this.engine !== "custom") {
|
|
125926
126182
|
this.clearHealthPoll();
|
|
125927
126183
|
return;
|
|
125928
126184
|
}
|
|
125929
126185
|
this.checkEngineReadiness()
|
|
125930
126186
|
.then(readiness => {
|
|
125931
126187
|
if (readiness === "ready") {
|
|
125932
|
-
this.
|
|
125933
|
-
|
|
125934
|
-
|
|
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}`));
|
|
125935
126203
|
}
|
|
125936
126204
|
})
|
|
125937
126205
|
.catch(() => {
|
|
@@ -125956,6 +126224,15 @@ class ModelManager extends EventEmitter {
|
|
|
125956
126224
|
this.lastEngineError = err;
|
|
125957
126225
|
this.emit("engineError", err);
|
|
125958
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
|
+
}
|
|
125959
126236
|
async releaseDownloadLock() {
|
|
125960
126237
|
const handle = this.downloadLockHandle;
|
|
125961
126238
|
if (!handle)
|
|
@@ -125993,6 +126270,8 @@ class ModelManager extends EventEmitter {
|
|
|
125993
126270
|
}));
|
|
125994
126271
|
});
|
|
125995
126272
|
processManager.on("stopped", (code, signal) => {
|
|
126273
|
+
this.lastEngineExitCode = code;
|
|
126274
|
+
this.lastEngineExitSignal = signal;
|
|
125996
126275
|
if (hasTerminated) {
|
|
125997
126276
|
return;
|
|
125998
126277
|
}
|
|
@@ -126020,6 +126299,8 @@ class ModelManager extends EventEmitter {
|
|
|
126020
126299
|
async startEngineProcess() {
|
|
126021
126300
|
const targetDir = join(this.modelsDirectory, this.uniqueName);
|
|
126022
126301
|
switch (this.engine) {
|
|
126302
|
+
case "custom":
|
|
126303
|
+
return null;
|
|
126023
126304
|
case "exllamav3":
|
|
126024
126305
|
return startExllamav3.call(this, {
|
|
126025
126306
|
enginePort: this.enginePort,
|
|
@@ -126060,6 +126341,69 @@ class ModelManager extends EventEmitter {
|
|
|
126060
126341
|
}
|
|
126061
126342
|
}
|
|
126062
126343
|
|
|
126344
|
+
// Ordered most-specific first: a message mentioning a prompt-size failure should classify as
|
|
126345
|
+
// "prompt" even if it also touches memory text; memory outranks config because OOM kills frequently
|
|
126346
|
+
// emit sparse stderr. These are heuristics, not exhaustively enumerated engine vocabularies.
|
|
126347
|
+
const OOM_PATTERNS = [
|
|
126348
|
+
/CUDA out of memory/i,
|
|
126349
|
+
/No available memory for the cache blocks/i,
|
|
126350
|
+
/\bOOMKilled\b/,
|
|
126351
|
+
/out of memory/i,
|
|
126352
|
+
/MemoryError/i,
|
|
126353
|
+
/Cannot allocate memory/i
|
|
126354
|
+
];
|
|
126355
|
+
const PROMPT_PATTERNS = [
|
|
126356
|
+
/prompt is too long/i,
|
|
126357
|
+
/maximum context length exceeded/i,
|
|
126358
|
+
/too many tokens/i
|
|
126359
|
+
];
|
|
126360
|
+
const CONFIG_PATTERNS = [
|
|
126361
|
+
/unrecognized argument/i,
|
|
126362
|
+
/invalid argument/i,
|
|
126363
|
+
/error while loading state_dict/i,
|
|
126364
|
+
/Architecture not understood/i,
|
|
126365
|
+
/No such file or directory/i
|
|
126366
|
+
];
|
|
126367
|
+
/**
|
|
126368
|
+
* Coarse engine-failure classification used only to fill `engine_execution.error_type`. Exit code and
|
|
126369
|
+
* terminating signal are consulted FIRST (SIGKILL/SIGSEGV are reliable OOM signals regardless of
|
|
126370
|
+
* how much stderr the engine produced); the message text then refines the category. Anything
|
|
126371
|
+
* unrecognized is "unknown".
|
|
126372
|
+
*/
|
|
126373
|
+
function classifyEngineFailure({ error, exitCode, signal }) {
|
|
126374
|
+
// 137 = SIGKILL (kernel OOM-killer), 139 = SIGSEGV (illegal memory access). Treat both as OOM
|
|
126375
|
+
// so that memory-pressure deaths do not degrade to "unknown" when stderr is truncated.
|
|
126376
|
+
if (exitCode === 137 || exitCode === 139) {
|
|
126377
|
+
return "oom";
|
|
126378
|
+
}
|
|
126379
|
+
// Direct OS kills (SIGKILL/SIGSEGV) leave no meaningful exit code; honor them like 137/139.
|
|
126380
|
+
if (signal === "SIGKILL" || signal === "SIGSEGV") {
|
|
126381
|
+
return "oom";
|
|
126382
|
+
}
|
|
126383
|
+
const text = `${error.message}`.slice(0, 4000);
|
|
126384
|
+
for (const pattern of PROMPT_PATTERNS) {
|
|
126385
|
+
if (pattern.test(text)) {
|
|
126386
|
+
return "prompt";
|
|
126387
|
+
}
|
|
126388
|
+
}
|
|
126389
|
+
for (const pattern of OOM_PATTERNS) {
|
|
126390
|
+
if (pattern.test(text)) {
|
|
126391
|
+
return "oom";
|
|
126392
|
+
}
|
|
126393
|
+
}
|
|
126394
|
+
for (const pattern of CONFIG_PATTERNS) {
|
|
126395
|
+
if (pattern.test(text)) {
|
|
126396
|
+
return "config";
|
|
126397
|
+
}
|
|
126398
|
+
}
|
|
126399
|
+
// A process that exited abnormally with no recognizable diagnostic: treat as a crash rather
|
|
126400
|
+
// than "unknown" so the two buckets distinguish "we saw nothing" from "it died badly".
|
|
126401
|
+
if (exitCode !== null && exitCode !== 0) {
|
|
126402
|
+
return "crash";
|
|
126403
|
+
}
|
|
126404
|
+
return "unknown";
|
|
126405
|
+
}
|
|
126406
|
+
|
|
126063
126407
|
const EXCEPTION_LINE_PATTERN = /([A-Za-z_][A-Za-z0-9_]*(?:Error|Exception)):\s*(.+)/;
|
|
126064
126408
|
const FALLBACK_DETAIL_MAX_LENGTH = 300;
|
|
126065
126409
|
const FALLBACK_RAW_MAX_LENGTH = 500;
|
|
@@ -126268,8 +126612,9 @@ function isEngineUsageChunk(value) {
|
|
|
126268
126612
|
}
|
|
126269
126613
|
return true;
|
|
126270
126614
|
}
|
|
126271
|
-
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 }) {
|
|
126272
126616
|
const startedAt = requestStartedAt ?? Date.now();
|
|
126617
|
+
const clientModelName = responseModelName ?? null;
|
|
126273
126618
|
const passThrough = new PassThrough();
|
|
126274
126619
|
passThrough.on("error", (error) => {
|
|
126275
126620
|
logger.error("Engine response stream error", {
|
|
@@ -126281,53 +126626,61 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126281
126626
|
let firstChunkAt = null;
|
|
126282
126627
|
let usage = null;
|
|
126283
126628
|
let buffer = "";
|
|
126629
|
+
let pendingFragment = "";
|
|
126284
126630
|
let completed = false;
|
|
126285
|
-
function
|
|
126286
|
-
const
|
|
126287
|
-
|
|
126288
|
-
|
|
126289
|
-
|
|
126290
|
-
|
|
126291
|
-
|
|
126292
|
-
|
|
126293
|
-
|
|
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;
|
|
126294
126645
|
}
|
|
126295
|
-
|
|
126296
|
-
|
|
126297
|
-
|
|
126298
|
-
|
|
126646
|
+
if (clientModelName !== null &&
|
|
126647
|
+
typeof parsed.model === "string" &&
|
|
126648
|
+
parsed.model !== clientModelName) {
|
|
126649
|
+
parsed.model = clientModelName;
|
|
126650
|
+
modified = true;
|
|
126299
126651
|
}
|
|
126300
|
-
|
|
126301
|
-
const
|
|
126302
|
-
|
|
126303
|
-
|
|
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;
|
|
126304
126663
|
modified = true;
|
|
126305
126664
|
}
|
|
126306
|
-
if (parsed.usage) {
|
|
126307
|
-
const usageChunk = parsed.usage;
|
|
126308
|
-
const effectiveContext = getEffectiveContextLength({
|
|
126309
|
-
contextLength,
|
|
126310
|
-
engineConfig,
|
|
126311
|
-
engineType
|
|
126312
|
-
});
|
|
126313
|
-
if (usageChunk.context_usage === undefined &&
|
|
126314
|
-
usageChunk.prompt_tokens !== undefined &&
|
|
126315
|
-
effectiveContext !== null) {
|
|
126316
|
-
usageChunk.context_usage = usageChunk.prompt_tokens / effectiveContext;
|
|
126317
|
-
modified = true;
|
|
126318
|
-
}
|
|
126319
|
-
}
|
|
126320
|
-
if (modified) {
|
|
126321
|
-
modifiedLines.push("data: " + JSON.stringify(parsed));
|
|
126322
|
-
continue;
|
|
126323
|
-
}
|
|
126324
126665
|
}
|
|
126325
|
-
|
|
126326
|
-
|
|
126666
|
+
if (modified) {
|
|
126667
|
+
return "data: " + JSON.stringify(parsed);
|
|
126327
126668
|
}
|
|
126328
|
-
modifiedLines.push(rawLine);
|
|
126329
126669
|
}
|
|
126330
|
-
|
|
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");
|
|
126331
126684
|
}
|
|
126332
126685
|
function parseUsageFromBuffer() {
|
|
126333
126686
|
const lines = buffer.split("\n");
|
|
@@ -126423,6 +126776,9 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126423
126776
|
if (buffer.length > 0) {
|
|
126424
126777
|
parseUsageFromBuffer();
|
|
126425
126778
|
}
|
|
126779
|
+
if (pendingFragment.length > 0) {
|
|
126780
|
+
passThrough.write(modifyChunkWithUsage(Buffer.from("", "utf8"), true));
|
|
126781
|
+
}
|
|
126426
126782
|
logEngineMetrics({
|
|
126427
126783
|
agentEngineType,
|
|
126428
126784
|
level: "info",
|
|
@@ -126465,9 +126821,11 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126465
126821
|
stream: passThrough
|
|
126466
126822
|
};
|
|
126467
126823
|
}
|
|
126468
|
-
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 }) {
|
|
126469
126825
|
const maxUsageCaptureBytes = 1024 * 1024;
|
|
126470
126826
|
const startedAt = requestStartedAt ?? Date.now();
|
|
126827
|
+
const clientModelName = responseModelName ?? null;
|
|
126828
|
+
const rewriteBuffer = clientModelName !== null ? [] : null;
|
|
126471
126829
|
const passThrough = new PassThrough();
|
|
126472
126830
|
passThrough.on("error", (error) => {
|
|
126473
126831
|
logger.error("Engine response stream error", {
|
|
@@ -126523,7 +126881,12 @@ function monitorEngineResponseSingle({ agentEngineType, body, contextLength, eng
|
|
|
126523
126881
|
usageChunks.length = 0;
|
|
126524
126882
|
}
|
|
126525
126883
|
}
|
|
126526
|
-
|
|
126884
|
+
if (rewriteBuffer) {
|
|
126885
|
+
rewriteBuffer.push(chunkBuffer);
|
|
126886
|
+
}
|
|
126887
|
+
else {
|
|
126888
|
+
passThrough.write(chunkBuffer);
|
|
126889
|
+
}
|
|
126527
126890
|
});
|
|
126528
126891
|
body.once("error", err => {
|
|
126529
126892
|
logEngineMetrics({
|
|
@@ -126583,6 +126946,24 @@ function monitorEngineResponseSingle({ agentEngineType, body, contextLength, eng
|
|
|
126583
126946
|
responseBytes,
|
|
126584
126947
|
usage
|
|
126585
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
|
+
}
|
|
126586
126967
|
finalize(null);
|
|
126587
126968
|
passThrough.end();
|
|
126588
126969
|
});
|
|
@@ -126719,7 +127100,7 @@ function applyChatTemplateKwargs({ body, model }) {
|
|
|
126719
127100
|
}
|
|
126720
127101
|
return payload;
|
|
126721
127102
|
}
|
|
126722
|
-
function serializeRequestBody$1(body, { model, path } = {}) {
|
|
127103
|
+
function serializeRequestBody$1(body, { model, path, servedModelName } = {}) {
|
|
126723
127104
|
if (!isPlainObject$a(body)) {
|
|
126724
127105
|
const payload = typeof body === "string" ? body : JSON.stringify(body);
|
|
126725
127106
|
return {
|
|
@@ -126728,6 +127109,9 @@ function serializeRequestBody$1(body, { model, path } = {}) {
|
|
|
126728
127109
|
};
|
|
126729
127110
|
}
|
|
126730
127111
|
let requestPayload = { ...body };
|
|
127112
|
+
if (servedModelName) {
|
|
127113
|
+
requestPayload.model = servedModelName;
|
|
127114
|
+
}
|
|
126731
127115
|
if (path === "/v1/chat/completions" && model) {
|
|
126732
127116
|
requestPayload = applyChatTemplateKwargs({ body: requestPayload, model });
|
|
126733
127117
|
}
|
|
@@ -126784,8 +127168,15 @@ async function proxyEmbeddingsRoute({ body, conduitConfiguration, endpointId, lo
|
|
|
126784
127168
|
}
|
|
126785
127169
|
const engineType = conduitConfiguration.engineConfig?.type ?? null;
|
|
126786
127170
|
const engineConfig = conduitConfiguration.engineConfig ?? null;
|
|
127171
|
+
const servedModelName = modelManager.resolvedServedModelName;
|
|
127172
|
+
const clientModelName = isPlainObject$a(body) && typeof body.model === "string" ? body.model : null;
|
|
126787
127173
|
const serializedBody = isPlainObject$a(body)
|
|
126788
|
-
? JSON.stringify(
|
|
127174
|
+
? JSON.stringify(servedModelName
|
|
127175
|
+
? {
|
|
127176
|
+
...body,
|
|
127177
|
+
model: servedModelName
|
|
127178
|
+
}
|
|
127179
|
+
: body)
|
|
126789
127180
|
: typeof body === "string"
|
|
126790
127181
|
? body
|
|
126791
127182
|
: JSON.stringify(body);
|
|
@@ -126903,7 +127294,8 @@ async function proxyEmbeddingsRoute({ body, conduitConfiguration, endpointId, lo
|
|
|
126903
127294
|
onComplete: onMonitoringComplete,
|
|
126904
127295
|
requestBodyBytes,
|
|
126905
127296
|
requestPath: "/v1/embeddings",
|
|
126906
|
-
requestStartedAt
|
|
127297
|
+
requestStartedAt,
|
|
127298
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
126907
127299
|
});
|
|
126908
127300
|
return {
|
|
126909
127301
|
body: monitoredResponse.stream,
|
|
@@ -126928,8 +127320,10 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
126928
127320
|
}
|
|
126929
127321
|
const engineType = conduitConfiguration.engineConfig?.type ?? null;
|
|
126930
127322
|
const engineConfig = conduitConfiguration.engineConfig ?? null;
|
|
127323
|
+
const servedModelName = modelManager.resolvedServedModelName;
|
|
127324
|
+
const clientModelName = isPlainObject$a(body) && typeof body.model === "string" ? body.model : null;
|
|
126931
127325
|
const effectiveBody = modelManager.model.multimodalEnabled ? body : stripImagesFromBody(body);
|
|
126932
|
-
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 });
|
|
126933
127327
|
const requestStartedAt = Date.now();
|
|
126934
127328
|
const requestBody = JSON.parse(serializedBody);
|
|
126935
127329
|
const streamRequested = requestBody.stream === true;
|
|
@@ -127069,7 +127463,8 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127069
127463
|
onComplete: onMonitoringComplete,
|
|
127070
127464
|
requestBodyBytes,
|
|
127071
127465
|
requestPath: path,
|
|
127072
|
-
requestStartedAt
|
|
127466
|
+
requestStartedAt,
|
|
127467
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127073
127468
|
})
|
|
127074
127469
|
: monitorEngineResponseSingle({
|
|
127075
127470
|
agentEngineType: engineType ?? "unknown",
|
|
@@ -127081,7 +127476,8 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127081
127476
|
onComplete: onMonitoringComplete,
|
|
127082
127477
|
requestBodyBytes,
|
|
127083
127478
|
requestPath: path,
|
|
127084
|
-
requestStartedAt
|
|
127479
|
+
requestStartedAt,
|
|
127480
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127085
127481
|
});
|
|
127086
127482
|
return {
|
|
127087
127483
|
body: monitoredResponse.stream,
|
|
@@ -138371,6 +138767,122 @@ async function detectDockerVersion() {
|
|
|
138371
138767
|
}
|
|
138372
138768
|
}
|
|
138373
138769
|
|
|
138770
|
+
/**
|
|
138771
|
+
* Flattens flat CLI extra-arg tokens into [arg, value] pairs, sorted by ARG NAME (ascending, ties by
|
|
138772
|
+
* value). `--flag=value` pairs split on the first `=`; a bare `--flag` consumes the following token
|
|
138773
|
+
* as its value when that token does not start with "-" (classic CLI convention); anything else
|
|
138774
|
+
* (flags, non-strings) is dropped. Secret-like option values are masked before pairing so they
|
|
138775
|
+
* never reach execution reports.
|
|
138776
|
+
*/
|
|
138777
|
+
function pairExtraArgs(tokens) {
|
|
138778
|
+
if (!Array.isArray(tokens)) {
|
|
138779
|
+
return [];
|
|
138780
|
+
}
|
|
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 : "")));
|
|
138784
|
+
const pairs = [];
|
|
138785
|
+
let index = 0;
|
|
138786
|
+
while (index < list.length) {
|
|
138787
|
+
const token = list[index];
|
|
138788
|
+
if (typeof token !== "string" || token.length === 0 || !token.startsWith("-")) {
|
|
138789
|
+
index++;
|
|
138790
|
+
continue;
|
|
138791
|
+
}
|
|
138792
|
+
const separator = token.indexOf("=");
|
|
138793
|
+
if (separator > -1) {
|
|
138794
|
+
const arg = token.slice(0, separator);
|
|
138795
|
+
if (arg.length > 0) {
|
|
138796
|
+
pairs.push([arg, token.slice(separator + 1)]);
|
|
138797
|
+
}
|
|
138798
|
+
index++;
|
|
138799
|
+
continue;
|
|
138800
|
+
}
|
|
138801
|
+
const next = list[index + 1];
|
|
138802
|
+
const consumesNext = typeof next === "string" && next.length > 0 && !next.startsWith("-");
|
|
138803
|
+
if (consumesNext) {
|
|
138804
|
+
pairs.push([token, next]);
|
|
138805
|
+
index += 2;
|
|
138806
|
+
}
|
|
138807
|
+
else {
|
|
138808
|
+
pairs.push([token, ""]);
|
|
138809
|
+
index++;
|
|
138810
|
+
}
|
|
138811
|
+
}
|
|
138812
|
+
return pairs.sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0);
|
|
138813
|
+
}
|
|
138814
|
+
/**
|
|
138815
|
+
* Files AT MOST ONE engine_execution report per engine startup. `beginStartup(runAt)` re-arms the
|
|
138816
|
+
* latch on every fresh model start (initial boot or cycle) and stamps the startup epoch used as the
|
|
138817
|
+
* row's `run_at`. Whichever of the three report* paths fires first wins: startup failure, in-flight
|
|
138818
|
+
* crash, or first successful full prompt completion.
|
|
138819
|
+
*/
|
|
138820
|
+
class EngineExecutionReporter {
|
|
138821
|
+
options;
|
|
138822
|
+
currentStartupAt = null;
|
|
138823
|
+
reportedForCurrentStartup = false;
|
|
138824
|
+
constructor(options) {
|
|
138825
|
+
this.options = options;
|
|
138826
|
+
}
|
|
138827
|
+
/** Re-arms the latch; the stamp becomes the row's `run_at` (moment startup began). */
|
|
138828
|
+
beginStartup(runAt) {
|
|
138829
|
+
this.currentStartupAt = runAt;
|
|
138830
|
+
this.reportedForCurrentStartup = false;
|
|
138831
|
+
}
|
|
138832
|
+
/** Reports a startup failure (rejected prepare/start, readiness timeout, pre-ready death). */
|
|
138833
|
+
async reportStartupFailure(report) {
|
|
138834
|
+
await this.file(report, false);
|
|
138835
|
+
}
|
|
138836
|
+
/** Reports a spontaneous crash of an engine that had reached the running state. */
|
|
138837
|
+
async reportRuntimeCrash(report) {
|
|
138838
|
+
await this.file(report, false);
|
|
138839
|
+
}
|
|
138840
|
+
/** Reports the first fully-responded, token-bearing prompt completion since startup. */
|
|
138841
|
+
async reportSuccess(report) {
|
|
138842
|
+
await this.file(report, true);
|
|
138843
|
+
}
|
|
138844
|
+
async file(report, success) {
|
|
138845
|
+
if (this.reportedForCurrentStartup || this.currentStartupAt === null) {
|
|
138846
|
+
return;
|
|
138847
|
+
}
|
|
138848
|
+
// Latch BEFORE the network call: a thrown POST cannot double-file for this startup.
|
|
138849
|
+
this.reportedForCurrentStartup = true;
|
|
138850
|
+
const context = this.options.buildContext();
|
|
138851
|
+
const payload = {
|
|
138852
|
+
avgTps: report.throughput.avgTps,
|
|
138853
|
+
completionTokens: report.usage.completionTokens,
|
|
138854
|
+
durationMs: report.durationMs,
|
|
138855
|
+
engineType: context.engineType,
|
|
138856
|
+
engineVersion: context.engineVersion,
|
|
138857
|
+
errorDetail: success ? null : report.errorDetail,
|
|
138858
|
+
errorType: success ? null : report.errorType,
|
|
138859
|
+
extraArgs: context.extraArgsPairs,
|
|
138860
|
+
finishedAtISO: new Date().toISOString(),
|
|
138861
|
+
peakTps: report.throughput.peakTps,
|
|
138862
|
+
promptTokens: report.usage.promptTokens,
|
|
138863
|
+
runAtISO: this.currentStartupAt.toISOString(),
|
|
138864
|
+
success,
|
|
138865
|
+
ttftMs: report.ttftMs,
|
|
138866
|
+
totalTokens: report.usage.totalTokens
|
|
138867
|
+
};
|
|
138868
|
+
try {
|
|
138869
|
+
await this.options.report(payload);
|
|
138870
|
+
this.options.logger.info("Engine execution outcome reported", {
|
|
138871
|
+
inferenceSourceID: this.options.sourceLabel,
|
|
138872
|
+
success
|
|
138873
|
+
});
|
|
138874
|
+
}
|
|
138875
|
+
catch (error) {
|
|
138876
|
+
// Losing one report is preferable to filing two; the latch stays latched.
|
|
138877
|
+
this.options.logger.warn("Failed to report engine execution outcome", {
|
|
138878
|
+
error: asError(error),
|
|
138879
|
+
inferenceSourceID: this.options.sourceLabel,
|
|
138880
|
+
success
|
|
138881
|
+
});
|
|
138882
|
+
}
|
|
138883
|
+
}
|
|
138884
|
+
}
|
|
138885
|
+
|
|
138374
138886
|
async function createApplication({ abortController, apiClient, configuration, logger }) {
|
|
138375
138887
|
ensureDockerValidEnv();
|
|
138376
138888
|
logger.info("Fetching conduit configuration");
|
|
@@ -138402,6 +138914,87 @@ async function createApplication({ abortController, apiClient, configuration, lo
|
|
|
138402
138914
|
error: asError(error)
|
|
138403
138915
|
});
|
|
138404
138916
|
}
|
|
138917
|
+
const reporter = new EngineExecutionReporter({
|
|
138918
|
+
buildContext: () => {
|
|
138919
|
+
const engineType = conduitConfiguration.engineConfig?.type ?? "llama.cpp";
|
|
138920
|
+
const versions = {
|
|
138921
|
+
custom: null,
|
|
138922
|
+
exllamav3: machine?.exllamav3Version ?? null,
|
|
138923
|
+
"llama.cpp": machine?.llamaCppVersion ?? null,
|
|
138924
|
+
"mlx-lm": machine?.mlxlmVersion ?? null,
|
|
138925
|
+
sglang: machine?.sglangVersion ?? null,
|
|
138926
|
+
"tensorrt-llm": machine?.tensorrtLlmVersion ?? null,
|
|
138927
|
+
vllm: machine?.vllmVersion ?? null
|
|
138928
|
+
};
|
|
138929
|
+
return {
|
|
138930
|
+
engineType,
|
|
138931
|
+
engineVersion: versions[engineType] ?? null,
|
|
138932
|
+
extraArgsPairs: pairExtraArgs(conduitConfiguration.engineConfig?.extraArgs)
|
|
138933
|
+
};
|
|
138934
|
+
},
|
|
138935
|
+
logger,
|
|
138936
|
+
report: payload => apiClient.reportEngineExecution(payload),
|
|
138937
|
+
sourceLabel: configuration.inferenceSourceID
|
|
138938
|
+
});
|
|
138939
|
+
// Intercept the prompt-metrics chokepoint so the first fully-responded, token-bearing prompt of
|
|
138940
|
+
// each fresh startup files the one-shot engine_execution success report. Handlers close over the
|
|
138941
|
+
// SAME `apiClient` object and read `reportPromptMetrics` at request-dispatch time (which always
|
|
138942
|
+
// follows this point), so the wrapped method is what they invoke.
|
|
138943
|
+
const rawReportPromptMetrics = apiClient.reportPromptMetrics;
|
|
138944
|
+
apiClient.reportPromptMetrics = async (payload) => {
|
|
138945
|
+
if (payload.successful && payload.completionTokens > 0 && payload.latencyMs > 0) {
|
|
138946
|
+
// The one-shot report is kicked off and its settlement attached HERE (before any await): if the
|
|
138947
|
+
// metrics path throws below, the report promise must still be able to log its own rejection.
|
|
138948
|
+
const successReport = reporter
|
|
138949
|
+
.reportSuccess({
|
|
138950
|
+
durationMs: payload.latencyMs,
|
|
138951
|
+
errorDetail: null,
|
|
138952
|
+
errorType: null,
|
|
138953
|
+
throughput: {
|
|
138954
|
+
avgTps: payload.tokensPerSecond,
|
|
138955
|
+
peakTps: null
|
|
138956
|
+
},
|
|
138957
|
+
ttftMs: payload.timeToFirstTokenMs ?? 0,
|
|
138958
|
+
usage: {
|
|
138959
|
+
completionTokens: payload.completionTokens,
|
|
138960
|
+
promptTokens: payload.promptTokens,
|
|
138961
|
+
totalTokens: payload.totalTokens
|
|
138962
|
+
}
|
|
138963
|
+
})
|
|
138964
|
+
.catch(error => {
|
|
138965
|
+
logger.warn("Engine execution success report failed", {
|
|
138966
|
+
error: asError(error)
|
|
138967
|
+
});
|
|
138968
|
+
});
|
|
138969
|
+
await rawReportPromptMetrics(payload);
|
|
138970
|
+
await successReport;
|
|
138971
|
+
return;
|
|
138972
|
+
}
|
|
138973
|
+
await rawReportPromptMetrics(payload);
|
|
138974
|
+
};
|
|
138975
|
+
const SECRET_ARG_MASK_PATTERN = /(-{1,2}[A-Za-z0-9_.]*(?:api[-_]?key|hf[-_]?token|token)(?:\s+|[=:]))\S+/gi;
|
|
138976
|
+
// Assembles the payload shared by the startup-failure and runtime-crash report paths. Stderr
|
|
138977
|
+
// may echo secrets, so mask `--api-key`/token-looking args before they reach the DB.
|
|
138978
|
+
function buildCrashReport(error, exitCode, signal) {
|
|
138979
|
+
const classification = classifyEngineFailure({ error, exitCode, signal });
|
|
138980
|
+
const raw = normalizeEngineError(error.message);
|
|
138981
|
+
const masked = raw.replace(SECRET_ARG_MASK_PATTERN, "$1***");
|
|
138982
|
+
return {
|
|
138983
|
+
durationMs: 0,
|
|
138984
|
+
errorDetail: masked.slice(0, 2048),
|
|
138985
|
+
errorType: classification,
|
|
138986
|
+
ttftMs: 0,
|
|
138987
|
+
throughput: {
|
|
138988
|
+
avgTps: 0,
|
|
138989
|
+
peakTps: null
|
|
138990
|
+
},
|
|
138991
|
+
usage: {
|
|
138992
|
+
completionTokens: 0,
|
|
138993
|
+
promptTokens: 0,
|
|
138994
|
+
totalTokens: 0
|
|
138995
|
+
}
|
|
138996
|
+
};
|
|
138997
|
+
}
|
|
138405
138998
|
const conduitStateManager = new ConduitStateManager({
|
|
138406
138999
|
initialState: {
|
|
138407
139000
|
state: "initialising"
|
|
@@ -138453,6 +139046,17 @@ async function createApplication({ abortController, apiClient, configuration, lo
|
|
|
138453
139046
|
});
|
|
138454
139047
|
stopRequestedByControl = false;
|
|
138455
139048
|
setErrorState({ error: normalizeEngineError(err.message) });
|
|
139049
|
+
// Spontaneous death of a SERVING engine → crash report, suppressed by the latch if the
|
|
139050
|
+
// startup's one-shot outcome was already filed. Startup-path failures report from
|
|
139051
|
+
// `startEngine`'s catch; this listener is the RUNTIME-crash path only.
|
|
139052
|
+
if (modelManager.wasRunning && !err.message.includes("interrupted by stop request")) {
|
|
139053
|
+
const crashReport = buildCrashReport(err, modelManager.lastExitCode, modelManager.lastExitSignal);
|
|
139054
|
+
reporter.reportRuntimeCrash(crashReport).catch(crashReportError => {
|
|
139055
|
+
logger.warn("Engine execution crash report failed", {
|
|
139056
|
+
error: asError(crashReportError)
|
|
139057
|
+
});
|
|
139058
|
+
});
|
|
139059
|
+
}
|
|
138456
139060
|
});
|
|
138457
139061
|
modelManager.on("engineReady", () => {
|
|
138458
139062
|
setOnlineState();
|
|
@@ -138512,24 +139116,40 @@ async function createApplication({ abortController, apiClient, configuration, lo
|
|
|
138512
139116
|
};
|
|
138513
139117
|
async function startEngine() {
|
|
138514
139118
|
logger.info("Engine start requested");
|
|
138515
|
-
|
|
138516
|
-
|
|
138517
|
-
|
|
138518
|
-
|
|
138519
|
-
|
|
138520
|
-
|
|
138521
|
-
|
|
139119
|
+
reporter.beginStartup(new Date());
|
|
139120
|
+
try {
|
|
139121
|
+
conduitStateManager.setState({
|
|
139122
|
+
modelFileName,
|
|
139123
|
+
modelName,
|
|
139124
|
+
state: "downloadingModelFiles",
|
|
139125
|
+
totalProgress: {
|
|
139126
|
+
file: 0,
|
|
139127
|
+
total: 0
|
|
139128
|
+
}
|
|
139129
|
+
});
|
|
139130
|
+
await conduitStateReportManager.reportNow();
|
|
139131
|
+
await modelManager.prepare({
|
|
139132
|
+
onDownloadProgress: reportDownloadProgress
|
|
139133
|
+
});
|
|
139134
|
+
conduitStateManager.setState({
|
|
139135
|
+
state: "bootingEngine"
|
|
139136
|
+
});
|
|
139137
|
+
await conduitStateReportManager.reportNow();
|
|
139138
|
+
await modelManager.start();
|
|
139139
|
+
}
|
|
139140
|
+
catch (error) {
|
|
139141
|
+
const parsedError = asError(error);
|
|
139142
|
+
// Operator-initiated aborts are not startup failures worth reporting.
|
|
139143
|
+
if (!parsedError.message.includes("interrupted by stop request")) {
|
|
139144
|
+
const startupReport = buildCrashReport(parsedError, modelManager.lastExitCode, modelManager.lastExitSignal);
|
|
139145
|
+
reporter.reportStartupFailure(startupReport).catch(startupReportError => {
|
|
139146
|
+
logger.warn("Engine execution startup report failed", {
|
|
139147
|
+
error: asError(startupReportError)
|
|
139148
|
+
});
|
|
139149
|
+
});
|
|
138522
139150
|
}
|
|
138523
|
-
|
|
138524
|
-
|
|
138525
|
-
await modelManager.prepare({
|
|
138526
|
-
onDownloadProgress: reportDownloadProgress
|
|
138527
|
-
});
|
|
138528
|
-
conduitStateManager.setState({
|
|
138529
|
-
state: "bootingEngine"
|
|
138530
|
-
});
|
|
138531
|
-
await conduitStateReportManager.reportNow();
|
|
138532
|
-
await modelManager.start();
|
|
139151
|
+
throw error;
|
|
139152
|
+
}
|
|
138533
139153
|
}
|
|
138534
139154
|
async function stopEngine({ reason }) {
|
|
138535
139155
|
if (!modelManager.canStop) {
|
|
@@ -138785,7 +139405,11 @@ function createModelManagerFromConfig(conduitConfiguration, configuration, logge
|
|
|
138785
139405
|
return new ModelManager({
|
|
138786
139406
|
contextLength: conduitConfiguration.contextLength ?? null,
|
|
138787
139407
|
engineConfig: engineConfig
|
|
138788
|
-
? {
|
|
139408
|
+
? {
|
|
139409
|
+
baseUrl: engineConfig.baseUrl ?? null,
|
|
139410
|
+
extraArgs: engineConfig.extraArgs,
|
|
139411
|
+
type: engineConfig.type
|
|
139412
|
+
}
|
|
138789
139413
|
: null,
|
|
138790
139414
|
enginePort: configuration.enginePort,
|
|
138791
139415
|
engineType: engineConfig?.type ?? "llama.cpp",
|
|
@@ -138796,7 +139420,11 @@ function createModelManagerFromConfig(conduitConfiguration, configuration, logge
|
|
|
138796
139420
|
}
|
|
138797
139421
|
function getConduitModelFileName(configuration) {
|
|
138798
139422
|
const { source } = configuration.targetModel;
|
|
138799
|
-
|
|
139423
|
+
if (source.type === "huggingface")
|
|
139424
|
+
return source.slug;
|
|
139425
|
+
if (source.type === "storage")
|
|
139426
|
+
return source.irid;
|
|
139427
|
+
return configuration.targetModel.id;
|
|
138800
139428
|
}
|
|
138801
139429
|
function getConduitModelName(configuration) {
|
|
138802
139430
|
return configuration.targetModel.id;
|