@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.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,
|
|
@@ -19986,6 +20008,32 @@ object$5({
|
|
|
19986
20008
|
sizeBytes: number$1().int().nonnegative().nullable()
|
|
19987
20009
|
});
|
|
19988
20010
|
|
|
20011
|
+
const EngineExecutionErrorTypeSchema = _enum$1([
|
|
20012
|
+
"config",
|
|
20013
|
+
"crash",
|
|
20014
|
+
"oom",
|
|
20015
|
+
"prompt",
|
|
20016
|
+
"unknown"
|
|
20017
|
+
]);
|
|
20018
|
+
const EngineExecutionReportPayloadSchema = object$5({
|
|
20019
|
+
avgTps: number$1().nonnegative().finite().default(0),
|
|
20020
|
+
completionTokens: number$1().int().nonnegative().default(0),
|
|
20021
|
+
durationMs: number$1().int().nonnegative().default(0),
|
|
20022
|
+
engineType: LLMEngineSchema.nullable(),
|
|
20023
|
+
engineVersion: string$2().max(64).nullable().default(null),
|
|
20024
|
+
errorDetail: string$2().max(2048).nullable().default(null),
|
|
20025
|
+
errorType: EngineExecutionErrorTypeSchema.nullable(),
|
|
20026
|
+
extraArgs: array$1(tuple([string$2().min(1).max(128), string$2().max(512)]))
|
|
20027
|
+
.max(256)
|
|
20028
|
+
.default([]),
|
|
20029
|
+
finishedAtISO: string$2().datetime({ offset: true }),
|
|
20030
|
+
peakTps: number$1().nonnegative().finite().nullable().default(null),
|
|
20031
|
+
promptTokens: number$1().int().nonnegative().default(0),
|
|
20032
|
+
runAtISO: string$2().datetime({ offset: true }),
|
|
20033
|
+
success: boolean$1(),
|
|
20034
|
+
ttftMs: number$1().int().nonnegative().default(0),
|
|
20035
|
+
totalTokens: number$1().int().nonnegative().default(0)
|
|
20036
|
+
});
|
|
19989
20037
|
const InferenceAgentLLMMetricsPayloadSchema = object$5({
|
|
19990
20038
|
bytes: number$1().int().nonnegative(),
|
|
19991
20039
|
completionTokens: number$1().int().nonnegative(),
|
|
@@ -20324,6 +20372,23 @@ const API_SERVICE_CONDUIT_API_REFERENCE = {
|
|
|
20324
20372
|
}
|
|
20325
20373
|
}
|
|
20326
20374
|
},
|
|
20375
|
+
"/conduit/api/v1/source/:sourceID/engine/execution": {
|
|
20376
|
+
POST: {
|
|
20377
|
+
auth: {
|
|
20378
|
+
type: "api-key"
|
|
20379
|
+
},
|
|
20380
|
+
body: EngineExecutionReportPayloadSchema,
|
|
20381
|
+
parameters: {
|
|
20382
|
+
sourceID: ULIDSchema
|
|
20383
|
+
},
|
|
20384
|
+
response: {
|
|
20385
|
+
schema: object$5({
|
|
20386
|
+
acknowledged: literal(true)
|
|
20387
|
+
}),
|
|
20388
|
+
type: "rest"
|
|
20389
|
+
}
|
|
20390
|
+
}
|
|
20391
|
+
},
|
|
20327
20392
|
"/conduit/api/v1/source/:sourceID/requests/:requestID/chunk": {
|
|
20328
20393
|
POST: {
|
|
20329
20394
|
auth: {
|
|
@@ -21076,7 +21141,8 @@ const CreateModelResponseSchema = object$5({
|
|
|
21076
21141
|
const CreateSourceBodySchema = object$5({
|
|
21077
21142
|
contextLength: number$1().int().positive().max(1048576).optional(),
|
|
21078
21143
|
engineId: ULIDSchema,
|
|
21079
|
-
|
|
21144
|
+
// Optional for custom engines, which serve externally managed models
|
|
21145
|
+
modelID: ULIDSchema.nullable().optional(),
|
|
21080
21146
|
name: ResourceNameSchema,
|
|
21081
21147
|
quantizationLabel: string$2().min(1).max(128).optional()
|
|
21082
21148
|
});
|
|
@@ -21125,7 +21191,7 @@ const SourceDetailResponseSchema = object$5({
|
|
|
21125
21191
|
const UpdateSourceBodySchema = object$5({
|
|
21126
21192
|
contextLength: number$1().int().positive().nullable().optional(),
|
|
21127
21193
|
engineId: ULIDSchema.nullable().optional(),
|
|
21128
|
-
modelID: ULIDSchema.optional(),
|
|
21194
|
+
modelID: ULIDSchema.nullable().optional(),
|
|
21129
21195
|
name: ResourceNameSchema.optional(),
|
|
21130
21196
|
quantizationLabel: string$2().min(1).max(128).nullable().optional()
|
|
21131
21197
|
});
|
|
@@ -21249,6 +21315,7 @@ const CreateEndpointResponseSchema = object$5({
|
|
|
21249
21315
|
id: ULIDSchema
|
|
21250
21316
|
});
|
|
21251
21317
|
const EngineOutputSchema = object$5({
|
|
21318
|
+
baseUrl: string$2().nullable(),
|
|
21252
21319
|
created: string$2(),
|
|
21253
21320
|
extraArgs: array$1(string$2()),
|
|
21254
21321
|
id: ULIDSchema,
|
|
@@ -21257,11 +21324,29 @@ const EngineOutputSchema = object$5({
|
|
|
21257
21324
|
updated: string$2()
|
|
21258
21325
|
});
|
|
21259
21326
|
const CreateEngineBodySchema = object$5({
|
|
21327
|
+
baseUrl: string$2().url().nullable().optional(),
|
|
21260
21328
|
extraArgs: array$1(string$2()).optional(),
|
|
21261
21329
|
name: ResourceNameSchema,
|
|
21262
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
|
+
}
|
|
21263
21347
|
});
|
|
21264
21348
|
const UpdateEngineBodySchema = object$5({
|
|
21349
|
+
baseUrl: string$2().url().nullable().optional(),
|
|
21265
21350
|
extraArgs: array$1(string$2()).optional(),
|
|
21266
21351
|
name: ResourceNameSchema.optional(),
|
|
21267
21352
|
type: LLMEngineSchema.optional()
|
|
@@ -22067,6 +22152,11 @@ const RecommendedModelSchema = object$5({
|
|
|
22067
22152
|
const recommendedModels = RecommendedModelSchema.array().parse(modelsData);
|
|
22068
22153
|
|
|
22069
22154
|
const ENGINE_API_COMPATIBILITY = {
|
|
22155
|
+
custom: {
|
|
22156
|
+
nativeAnthropicMessages: false,
|
|
22157
|
+
supportsEmbeddings: true,
|
|
22158
|
+
supportsVision: true
|
|
22159
|
+
},
|
|
22070
22160
|
exllamav3: {
|
|
22071
22161
|
nativeAnthropicMessages: false,
|
|
22072
22162
|
supportsEmbeddings: false,
|
|
@@ -111716,6 +111806,27 @@ function registerEndpointCommands({ program }) {
|
|
|
111716
111806
|
}
|
|
111717
111807
|
|
|
111718
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
|
+
}
|
|
111719
111830
|
function buildEngineCreateBody(options) {
|
|
111720
111831
|
if (!options.name) {
|
|
111721
111832
|
throw new Error("--name is required");
|
|
@@ -111726,7 +111837,12 @@ function buildEngineCreateBody(options) {
|
|
|
111726
111837
|
if (!ENGINE_TYPES.includes(options.type)) {
|
|
111727
111838
|
throw new Error(`Invalid engine type: ${options.type} (expected one of: ${ENGINE_TYPES.join(", ")})`);
|
|
111728
111839
|
}
|
|
111840
|
+
const baseUrl = validateEngineBaseURL({
|
|
111841
|
+
baseUrl: options.baseUrl,
|
|
111842
|
+
type: options.type
|
|
111843
|
+
});
|
|
111729
111844
|
return {
|
|
111845
|
+
baseUrl,
|
|
111730
111846
|
extraArgs: options.arg ?? [],
|
|
111731
111847
|
name: options.name,
|
|
111732
111848
|
type: options.type
|
|
@@ -111744,6 +111860,24 @@ function buildEngineUpdateBody(options) {
|
|
|
111744
111860
|
}
|
|
111745
111861
|
if (options.arg !== undefined)
|
|
111746
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
|
+
}
|
|
111747
111881
|
return body;
|
|
111748
111882
|
}
|
|
111749
111883
|
|
|
@@ -111757,10 +111891,11 @@ function registerEngineCommands({ program }) {
|
|
|
111757
111891
|
.description("Create or update an inference engine resource")
|
|
111758
111892
|
.option("--api-url <url>", "API base URL (required, no environment variable fallback)")
|
|
111759
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)")
|
|
111760
111895
|
.option("--id <ulid>", "Target an existing engine by ID (requires --update)")
|
|
111761
111896
|
.option("--key <value>", "API key (required, no environment variable fallback)")
|
|
111762
111897
|
.option("--name <name>", "Engine name (matched by --update)")
|
|
111763
|
-
.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")
|
|
111764
111899
|
.option("--update", "Update an existing engine matched by name (or --id) instead of erroring")
|
|
111765
111900
|
.action(async (options) => {
|
|
111766
111901
|
const { apiURL, apiKey } = resolveManagementConnection(options);
|
|
@@ -113781,6 +113916,19 @@ function createAPIClient({ apiKey, apiURL, inferenceSourceID, logger }) {
|
|
|
113781
113916
|
route: "/conduit/api/v1/source/:sourceID/state"
|
|
113782
113917
|
});
|
|
113783
113918
|
},
|
|
113919
|
+
reportEngineExecution: async (payload) => {
|
|
113920
|
+
await fetchByReference({
|
|
113921
|
+
baseURL: apiURL,
|
|
113922
|
+
body: payload,
|
|
113923
|
+
fetch: fetchWithAPIKey,
|
|
113924
|
+
method: "POST",
|
|
113925
|
+
parameters: {
|
|
113926
|
+
sourceID: inferenceSourceID
|
|
113927
|
+
},
|
|
113928
|
+
reference: API_SERVICE_CONDUIT_API_REFERENCE,
|
|
113929
|
+
route: "/conduit/api/v1/source/:sourceID/engine/execution"
|
|
113930
|
+
});
|
|
113931
|
+
},
|
|
113784
113932
|
reportPromptMetrics: async (payload) => {
|
|
113785
113933
|
await fetchByReference({
|
|
113786
113934
|
baseURL: apiURL,
|
|
@@ -117747,6 +117895,9 @@ async function getChatTemplateEngineArgs({ engine, model, targetDirectory }) {
|
|
|
117747
117895
|
return [];
|
|
117748
117896
|
const flag = FLAG_BASED_ENGINE_ARGS[engine];
|
|
117749
117897
|
if (!flag) {
|
|
117898
|
+
if (engine === "custom") {
|
|
117899
|
+
console.warn("[chatTemplate] Custom engines manage their own serving; ignoring chat template override");
|
|
117900
|
+
}
|
|
117750
117901
|
if (engine === "tensorrt-llm") {
|
|
117751
117902
|
console.warn("[chatTemplate] TensorRT-LLM does not support chat template overrides; ignoring");
|
|
117752
117903
|
}
|
|
@@ -124644,7 +124795,7 @@ function matchesQuantizationVariant({ filePath, variant }) {
|
|
|
124644
124795
|
return segments.slice(0, -1).some(segment => matcher.test(segment));
|
|
124645
124796
|
}
|
|
124646
124797
|
async function findQuantizedModelTarget({ model, path }) {
|
|
124647
|
-
if (model.source.type
|
|
124798
|
+
if (model.source.type !== "huggingface") {
|
|
124648
124799
|
throw new Error("Model storage not supported yet");
|
|
124649
124800
|
}
|
|
124650
124801
|
if (model.format !== "gguf") {
|
|
@@ -125559,7 +125710,11 @@ function sanitizeSegment(value) {
|
|
|
125559
125710
|
.replace(new RegExp(`${SEPARATOR}{2,}`, "g"), SEPARATOR);
|
|
125560
125711
|
}
|
|
125561
125712
|
function createModelStorageKey(model) {
|
|
125562
|
-
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;
|
|
125563
125718
|
return `${model.source.type}${SEPARATOR}${sanitizeSegment(identifier)}`;
|
|
125564
125719
|
}
|
|
125565
125720
|
|
|
@@ -125635,12 +125790,16 @@ class ModelManager extends EventEmitter {
|
|
|
125635
125790
|
uniqueName;
|
|
125636
125791
|
contextLength;
|
|
125637
125792
|
logger;
|
|
125793
|
+
discoveredModelNames = [];
|
|
125638
125794
|
engineProcess = null;
|
|
125639
125795
|
healthPollInterval = null;
|
|
125640
125796
|
lastEngineError = null;
|
|
125641
125797
|
lifecycleState = "stopped";
|
|
125642
125798
|
downloadLockHandle = null;
|
|
125643
125799
|
stopRequested = false;
|
|
125800
|
+
lastEngineExitCode = null;
|
|
125801
|
+
lastEngineExitSignal = null;
|
|
125802
|
+
reachedRunningState = false;
|
|
125644
125803
|
modelsDirectory;
|
|
125645
125804
|
constructor({ contextLength, engineConfig, enginePort, engineType, logger, model, root }) {
|
|
125646
125805
|
super();
|
|
@@ -125664,6 +125823,7 @@ class ModelManager extends EventEmitter {
|
|
|
125664
125823
|
}
|
|
125665
125824
|
async fetchOpenAI(path, opts) {
|
|
125666
125825
|
switch (this.engine) {
|
|
125826
|
+
case "custom":
|
|
125667
125827
|
case "exllamav3":
|
|
125668
125828
|
case "llama.cpp":
|
|
125669
125829
|
case "mlx-lm":
|
|
@@ -125671,6 +125831,9 @@ class ModelManager extends EventEmitter {
|
|
|
125671
125831
|
case "tensorrt-llm":
|
|
125672
125832
|
case "vllm": {
|
|
125673
125833
|
this.logger.debug(`Fetching from engine: ${path}`);
|
|
125834
|
+
const baseURL = this.engine === "custom"
|
|
125835
|
+
? this.requireCustomBaseURL()
|
|
125836
|
+
: `http://localhost:${this.enginePort}`;
|
|
125674
125837
|
const callerSignal = opts?.signal;
|
|
125675
125838
|
const controller = new AbortController();
|
|
125676
125839
|
const timeout = setTimeout(() => {
|
|
@@ -125681,7 +125844,7 @@ class ModelManager extends EventEmitter {
|
|
|
125681
125844
|
: controller.signal;
|
|
125682
125845
|
try {
|
|
125683
125846
|
const fetchStartedAt = Date.now();
|
|
125684
|
-
const response = await undiciExports.fetch(joinURL(
|
|
125847
|
+
const response = await undiciExports.fetch(joinURL(baseURL, path), {
|
|
125685
125848
|
...opts,
|
|
125686
125849
|
dispatcher: ENGINE_AGENT,
|
|
125687
125850
|
headers: {
|
|
@@ -125716,6 +125879,11 @@ class ModelManager extends EventEmitter {
|
|
|
125716
125879
|
modelID: this.model.id
|
|
125717
125880
|
});
|
|
125718
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;
|
|
125719
125887
|
case "exllamav3":
|
|
125720
125888
|
case "llama.cpp":
|
|
125721
125889
|
case "mlx-lm":
|
|
@@ -125763,12 +125931,17 @@ class ModelManager extends EventEmitter {
|
|
|
125763
125931
|
this.lifecycleState = "starting";
|
|
125764
125932
|
this.lastEngineError = null;
|
|
125765
125933
|
this.stopRequested = false;
|
|
125934
|
+
this.lastEngineExitCode = null;
|
|
125935
|
+
this.lastEngineExitSignal = null;
|
|
125936
|
+
this.reachedRunningState = false;
|
|
125766
125937
|
this.logger.info("Starting LLM engine", {
|
|
125767
125938
|
agentEngineType: this.engine
|
|
125768
125939
|
});
|
|
125769
125940
|
try {
|
|
125770
125941
|
this.engineProcess = await this.startEngineProcess();
|
|
125771
|
-
|
|
125942
|
+
if (this.engineProcess) {
|
|
125943
|
+
this.bindEngineProcessEvents(this.engineProcess);
|
|
125944
|
+
}
|
|
125772
125945
|
this.logger.info("Started LLM engine", {
|
|
125773
125946
|
agentEngineType: this.engine
|
|
125774
125947
|
});
|
|
@@ -125787,13 +125960,17 @@ class ModelManager extends EventEmitter {
|
|
|
125787
125960
|
if (!alreadyEmitted) {
|
|
125788
125961
|
this.emit("engineError", err);
|
|
125789
125962
|
}
|
|
125790
|
-
if (this.engineProcess) {
|
|
125963
|
+
if (this.engineProcess || this.engine === "custom") {
|
|
125791
125964
|
this.startHealthPoll();
|
|
125792
125965
|
}
|
|
125793
125966
|
throw err;
|
|
125794
125967
|
}
|
|
125795
125968
|
this.lifecycleState = "running";
|
|
125969
|
+
this.reachedRunningState = true;
|
|
125796
125970
|
this.emit("engineReady");
|
|
125971
|
+
if (this.engine === "custom") {
|
|
125972
|
+
this.startHealthPoll();
|
|
125973
|
+
}
|
|
125797
125974
|
}
|
|
125798
125975
|
async stop() {
|
|
125799
125976
|
if (this.lifecycleState === "stopping") {
|
|
@@ -125814,9 +125991,12 @@ class ModelManager extends EventEmitter {
|
|
|
125814
125991
|
this.clearHealthPoll();
|
|
125815
125992
|
const processManager = this.engineProcess;
|
|
125816
125993
|
if (!processManager) {
|
|
125994
|
+
this.stopRequested = true;
|
|
125995
|
+
this.reachedRunningState = false;
|
|
125817
125996
|
this.lifecycleState = "stopped";
|
|
125818
125997
|
return;
|
|
125819
125998
|
}
|
|
125999
|
+
this.reachedRunningState = false;
|
|
125820
126000
|
this.lifecycleState = "stopping";
|
|
125821
126001
|
this.stopRequested = true;
|
|
125822
126002
|
await processManager.stop();
|
|
@@ -125829,11 +126009,34 @@ class ModelManager extends EventEmitter {
|
|
|
125829
126009
|
this.lifecycleState === "starting" ||
|
|
125830
126010
|
this.lifecycleState === "errored");
|
|
125831
126011
|
}
|
|
126012
|
+
get lastExitCode() {
|
|
126013
|
+
return this.lastEngineExitCode;
|
|
126014
|
+
}
|
|
126015
|
+
get lastExitSignal() {
|
|
126016
|
+
return this.lastEngineExitSignal;
|
|
126017
|
+
}
|
|
125832
126018
|
get state() {
|
|
125833
126019
|
return this.lifecycleState;
|
|
125834
126020
|
}
|
|
126021
|
+
get resolvedServedModelName() {
|
|
126022
|
+
if (this.engine !== "custom")
|
|
126023
|
+
return null;
|
|
126024
|
+
return this.discoveredModelNames[0] ?? null;
|
|
126025
|
+
}
|
|
126026
|
+
get wasRunning() {
|
|
126027
|
+
return this.reachedRunningState;
|
|
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
|
+
}
|
|
125835
126035
|
async checkEngineReadiness() {
|
|
125836
126036
|
switch (this.engine) {
|
|
126037
|
+
case "custom": {
|
|
126038
|
+
return this.checkCustomReadiness();
|
|
126039
|
+
}
|
|
125837
126040
|
case "llama.cpp": {
|
|
125838
126041
|
return this.checkLlamacppReadiness();
|
|
125839
126042
|
}
|
|
@@ -125850,6 +126053,51 @@ class ModelManager extends EventEmitter {
|
|
|
125850
126053
|
return "ready";
|
|
125851
126054
|
}
|
|
125852
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
|
+
}
|
|
125853
126101
|
async checkGenericHealthReadiness() {
|
|
125854
126102
|
try {
|
|
125855
126103
|
const response = await undiciExports.fetch(joinURL(`http://localhost:${this.enginePort}`, "/health"), {
|
|
@@ -125905,18 +126153,24 @@ class ModelManager extends EventEmitter {
|
|
|
125905
126153
|
}
|
|
125906
126154
|
}
|
|
125907
126155
|
async waitForEngineReady() {
|
|
125908
|
-
const maxWaitMs = 15 * 60 * 1000;
|
|
126156
|
+
const maxWaitMs = Number.parseInt(process.env.ENGINE_STARTUP_TIMEOUT_MS ?? "", 10) || 15 * 60 * 1000;
|
|
125909
126157
|
const pollIntervalMs = 2000;
|
|
125910
126158
|
const start = Date.now();
|
|
125911
126159
|
while (Date.now() - start < maxWaitMs) {
|
|
125912
|
-
if (this.lifecycleState === "stopping") {
|
|
126160
|
+
if (this.lifecycleState === "stopping" || this.stopRequested) {
|
|
125913
126161
|
throw new Error("LLM engine startup interrupted by stop request");
|
|
125914
126162
|
}
|
|
125915
|
-
if (!this.engineProcess) {
|
|
126163
|
+
if (!this.engineProcess && this.engine !== "custom") {
|
|
125916
126164
|
throw new Error("LLM engine process exited before readiness checks completed");
|
|
125917
126165
|
}
|
|
125918
126166
|
const readiness = await this.checkEngineReadiness();
|
|
125919
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
|
+
}
|
|
125920
126174
|
return;
|
|
125921
126175
|
}
|
|
125922
126176
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
@@ -125934,18 +126188,32 @@ class ModelManager extends EventEmitter {
|
|
|
125934
126188
|
}
|
|
125935
126189
|
startHealthPoll() {
|
|
125936
126190
|
this.clearHealthPoll();
|
|
125937
|
-
this.logger.info("Starting background health poll
|
|
126191
|
+
this.logger.info("Starting background engine health poll", {
|
|
126192
|
+
agentEngineType: this.engine
|
|
126193
|
+
});
|
|
125938
126194
|
this.healthPollInterval = setInterval(() => {
|
|
125939
|
-
if (!this.engineProcess) {
|
|
126195
|
+
if (!this.engineProcess && this.engine !== "custom") {
|
|
125940
126196
|
this.clearHealthPoll();
|
|
125941
126197
|
return;
|
|
125942
126198
|
}
|
|
125943
126199
|
this.checkEngineReadiness()
|
|
125944
126200
|
.then(readiness => {
|
|
125945
126201
|
if (readiness === "ready") {
|
|
125946
|
-
this.
|
|
125947
|
-
|
|
125948
|
-
|
|
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}`));
|
|
125949
126217
|
}
|
|
125950
126218
|
})
|
|
125951
126219
|
.catch(() => {
|
|
@@ -125970,6 +126238,15 @@ class ModelManager extends EventEmitter {
|
|
|
125970
126238
|
this.lastEngineError = err;
|
|
125971
126239
|
this.emit("engineError", err);
|
|
125972
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
|
+
}
|
|
125973
126250
|
async releaseDownloadLock() {
|
|
125974
126251
|
const handle = this.downloadLockHandle;
|
|
125975
126252
|
if (!handle)
|
|
@@ -126007,6 +126284,8 @@ class ModelManager extends EventEmitter {
|
|
|
126007
126284
|
}));
|
|
126008
126285
|
});
|
|
126009
126286
|
processManager.on("stopped", (code, signal) => {
|
|
126287
|
+
this.lastEngineExitCode = code;
|
|
126288
|
+
this.lastEngineExitSignal = signal;
|
|
126010
126289
|
if (hasTerminated) {
|
|
126011
126290
|
return;
|
|
126012
126291
|
}
|
|
@@ -126034,6 +126313,8 @@ class ModelManager extends EventEmitter {
|
|
|
126034
126313
|
async startEngineProcess() {
|
|
126035
126314
|
const targetDir = path$1.join(this.modelsDirectory, this.uniqueName);
|
|
126036
126315
|
switch (this.engine) {
|
|
126316
|
+
case "custom":
|
|
126317
|
+
return null;
|
|
126037
126318
|
case "exllamav3":
|
|
126038
126319
|
return startExllamav3.call(this, {
|
|
126039
126320
|
enginePort: this.enginePort,
|
|
@@ -126074,6 +126355,69 @@ class ModelManager extends EventEmitter {
|
|
|
126074
126355
|
}
|
|
126075
126356
|
}
|
|
126076
126357
|
|
|
126358
|
+
// Ordered most-specific first: a message mentioning a prompt-size failure should classify as
|
|
126359
|
+
// "prompt" even if it also touches memory text; memory outranks config because OOM kills frequently
|
|
126360
|
+
// emit sparse stderr. These are heuristics, not exhaustively enumerated engine vocabularies.
|
|
126361
|
+
const OOM_PATTERNS = [
|
|
126362
|
+
/CUDA out of memory/i,
|
|
126363
|
+
/No available memory for the cache blocks/i,
|
|
126364
|
+
/\bOOMKilled\b/,
|
|
126365
|
+
/out of memory/i,
|
|
126366
|
+
/MemoryError/i,
|
|
126367
|
+
/Cannot allocate memory/i
|
|
126368
|
+
];
|
|
126369
|
+
const PROMPT_PATTERNS = [
|
|
126370
|
+
/prompt is too long/i,
|
|
126371
|
+
/maximum context length exceeded/i,
|
|
126372
|
+
/too many tokens/i
|
|
126373
|
+
];
|
|
126374
|
+
const CONFIG_PATTERNS = [
|
|
126375
|
+
/unrecognized argument/i,
|
|
126376
|
+
/invalid argument/i,
|
|
126377
|
+
/error while loading state_dict/i,
|
|
126378
|
+
/Architecture not understood/i,
|
|
126379
|
+
/No such file or directory/i
|
|
126380
|
+
];
|
|
126381
|
+
/**
|
|
126382
|
+
* Coarse engine-failure classification used only to fill `engine_execution.error_type`. Exit code and
|
|
126383
|
+
* terminating signal are consulted FIRST (SIGKILL/SIGSEGV are reliable OOM signals regardless of
|
|
126384
|
+
* how much stderr the engine produced); the message text then refines the category. Anything
|
|
126385
|
+
* unrecognized is "unknown".
|
|
126386
|
+
*/
|
|
126387
|
+
function classifyEngineFailure({ error, exitCode, signal }) {
|
|
126388
|
+
// 137 = SIGKILL (kernel OOM-killer), 139 = SIGSEGV (illegal memory access). Treat both as OOM
|
|
126389
|
+
// so that memory-pressure deaths do not degrade to "unknown" when stderr is truncated.
|
|
126390
|
+
if (exitCode === 137 || exitCode === 139) {
|
|
126391
|
+
return "oom";
|
|
126392
|
+
}
|
|
126393
|
+
// Direct OS kills (SIGKILL/SIGSEGV) leave no meaningful exit code; honor them like 137/139.
|
|
126394
|
+
if (signal === "SIGKILL" || signal === "SIGSEGV") {
|
|
126395
|
+
return "oom";
|
|
126396
|
+
}
|
|
126397
|
+
const text = `${error.message}`.slice(0, 4000);
|
|
126398
|
+
for (const pattern of PROMPT_PATTERNS) {
|
|
126399
|
+
if (pattern.test(text)) {
|
|
126400
|
+
return "prompt";
|
|
126401
|
+
}
|
|
126402
|
+
}
|
|
126403
|
+
for (const pattern of OOM_PATTERNS) {
|
|
126404
|
+
if (pattern.test(text)) {
|
|
126405
|
+
return "oom";
|
|
126406
|
+
}
|
|
126407
|
+
}
|
|
126408
|
+
for (const pattern of CONFIG_PATTERNS) {
|
|
126409
|
+
if (pattern.test(text)) {
|
|
126410
|
+
return "config";
|
|
126411
|
+
}
|
|
126412
|
+
}
|
|
126413
|
+
// A process that exited abnormally with no recognizable diagnostic: treat as a crash rather
|
|
126414
|
+
// than "unknown" so the two buckets distinguish "we saw nothing" from "it died badly".
|
|
126415
|
+
if (exitCode !== null && exitCode !== 0) {
|
|
126416
|
+
return "crash";
|
|
126417
|
+
}
|
|
126418
|
+
return "unknown";
|
|
126419
|
+
}
|
|
126420
|
+
|
|
126077
126421
|
const EXCEPTION_LINE_PATTERN = /([A-Za-z_][A-Za-z0-9_]*(?:Error|Exception)):\s*(.+)/;
|
|
126078
126422
|
const FALLBACK_DETAIL_MAX_LENGTH = 300;
|
|
126079
126423
|
const FALLBACK_RAW_MAX_LENGTH = 500;
|
|
@@ -126282,8 +126626,9 @@ function isEngineUsageChunk(value) {
|
|
|
126282
126626
|
}
|
|
126283
126627
|
return true;
|
|
126284
126628
|
}
|
|
126285
|
-
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 }) {
|
|
126286
126630
|
const startedAt = requestStartedAt ?? Date.now();
|
|
126631
|
+
const clientModelName = responseModelName ?? null;
|
|
126287
126632
|
const passThrough = new require$$0$8.PassThrough();
|
|
126288
126633
|
passThrough.on("error", (error) => {
|
|
126289
126634
|
logger.error("Engine response stream error", {
|
|
@@ -126295,53 +126640,61 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126295
126640
|
let firstChunkAt = null;
|
|
126296
126641
|
let usage = null;
|
|
126297
126642
|
let buffer = "";
|
|
126643
|
+
let pendingFragment = "";
|
|
126298
126644
|
let completed = false;
|
|
126299
|
-
function
|
|
126300
|
-
const
|
|
126301
|
-
|
|
126302
|
-
|
|
126303
|
-
|
|
126304
|
-
|
|
126305
|
-
|
|
126306
|
-
|
|
126307
|
-
|
|
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;
|
|
126308
126659
|
}
|
|
126309
|
-
|
|
126310
|
-
|
|
126311
|
-
|
|
126312
|
-
|
|
126660
|
+
if (clientModelName !== null &&
|
|
126661
|
+
typeof parsed.model === "string" &&
|
|
126662
|
+
parsed.model !== clientModelName) {
|
|
126663
|
+
parsed.model = clientModelName;
|
|
126664
|
+
modified = true;
|
|
126313
126665
|
}
|
|
126314
|
-
|
|
126315
|
-
const
|
|
126316
|
-
|
|
126317
|
-
|
|
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;
|
|
126318
126677
|
modified = true;
|
|
126319
126678
|
}
|
|
126320
|
-
if (parsed.usage) {
|
|
126321
|
-
const usageChunk = parsed.usage;
|
|
126322
|
-
const effectiveContext = getEffectiveContextLength({
|
|
126323
|
-
contextLength,
|
|
126324
|
-
engineConfig,
|
|
126325
|
-
engineType
|
|
126326
|
-
});
|
|
126327
|
-
if (usageChunk.context_usage === undefined &&
|
|
126328
|
-
usageChunk.prompt_tokens !== undefined &&
|
|
126329
|
-
effectiveContext !== null) {
|
|
126330
|
-
usageChunk.context_usage = usageChunk.prompt_tokens / effectiveContext;
|
|
126331
|
-
modified = true;
|
|
126332
|
-
}
|
|
126333
|
-
}
|
|
126334
|
-
if (modified) {
|
|
126335
|
-
modifiedLines.push("data: " + JSON.stringify(parsed));
|
|
126336
|
-
continue;
|
|
126337
|
-
}
|
|
126338
126679
|
}
|
|
126339
|
-
|
|
126340
|
-
|
|
126680
|
+
if (modified) {
|
|
126681
|
+
return "data: " + JSON.stringify(parsed);
|
|
126341
126682
|
}
|
|
126342
|
-
modifiedLines.push(rawLine);
|
|
126343
126683
|
}
|
|
126344
|
-
|
|
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");
|
|
126345
126698
|
}
|
|
126346
126699
|
function parseUsageFromBuffer() {
|
|
126347
126700
|
const lines = buffer.split("\n");
|
|
@@ -126437,6 +126790,9 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126437
126790
|
if (buffer.length > 0) {
|
|
126438
126791
|
parseUsageFromBuffer();
|
|
126439
126792
|
}
|
|
126793
|
+
if (pendingFragment.length > 0) {
|
|
126794
|
+
passThrough.write(modifyChunkWithUsage(Buffer.from("", "utf8"), true));
|
|
126795
|
+
}
|
|
126440
126796
|
logEngineMetrics({
|
|
126441
126797
|
agentEngineType,
|
|
126442
126798
|
level: "info",
|
|
@@ -126479,9 +126835,11 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126479
126835
|
stream: passThrough
|
|
126480
126836
|
};
|
|
126481
126837
|
}
|
|
126482
|
-
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 }) {
|
|
126483
126839
|
const maxUsageCaptureBytes = 1024 * 1024;
|
|
126484
126840
|
const startedAt = requestStartedAt ?? Date.now();
|
|
126841
|
+
const clientModelName = responseModelName ?? null;
|
|
126842
|
+
const rewriteBuffer = clientModelName !== null ? [] : null;
|
|
126485
126843
|
const passThrough = new require$$0$8.PassThrough();
|
|
126486
126844
|
passThrough.on("error", (error) => {
|
|
126487
126845
|
logger.error("Engine response stream error", {
|
|
@@ -126537,7 +126895,12 @@ function monitorEngineResponseSingle({ agentEngineType, body, contextLength, eng
|
|
|
126537
126895
|
usageChunks.length = 0;
|
|
126538
126896
|
}
|
|
126539
126897
|
}
|
|
126540
|
-
|
|
126898
|
+
if (rewriteBuffer) {
|
|
126899
|
+
rewriteBuffer.push(chunkBuffer);
|
|
126900
|
+
}
|
|
126901
|
+
else {
|
|
126902
|
+
passThrough.write(chunkBuffer);
|
|
126903
|
+
}
|
|
126541
126904
|
});
|
|
126542
126905
|
body.once("error", err => {
|
|
126543
126906
|
logEngineMetrics({
|
|
@@ -126597,6 +126960,24 @@ function monitorEngineResponseSingle({ agentEngineType, body, contextLength, eng
|
|
|
126597
126960
|
responseBytes,
|
|
126598
126961
|
usage
|
|
126599
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
|
+
}
|
|
126600
126981
|
finalize(null);
|
|
126601
126982
|
passThrough.end();
|
|
126602
126983
|
});
|
|
@@ -126733,7 +127114,7 @@ function applyChatTemplateKwargs({ body, model }) {
|
|
|
126733
127114
|
}
|
|
126734
127115
|
return payload;
|
|
126735
127116
|
}
|
|
126736
|
-
function serializeRequestBody$1(body, { model, path } = {}) {
|
|
127117
|
+
function serializeRequestBody$1(body, { model, path, servedModelName } = {}) {
|
|
126737
127118
|
if (!isPlainObject$a(body)) {
|
|
126738
127119
|
const payload = typeof body === "string" ? body : JSON.stringify(body);
|
|
126739
127120
|
return {
|
|
@@ -126742,6 +127123,9 @@ function serializeRequestBody$1(body, { model, path } = {}) {
|
|
|
126742
127123
|
};
|
|
126743
127124
|
}
|
|
126744
127125
|
let requestPayload = { ...body };
|
|
127126
|
+
if (servedModelName) {
|
|
127127
|
+
requestPayload.model = servedModelName;
|
|
127128
|
+
}
|
|
126745
127129
|
if (path === "/v1/chat/completions" && model) {
|
|
126746
127130
|
requestPayload = applyChatTemplateKwargs({ body: requestPayload, model });
|
|
126747
127131
|
}
|
|
@@ -126798,8 +127182,15 @@ async function proxyEmbeddingsRoute({ body, conduitConfiguration, endpointId, lo
|
|
|
126798
127182
|
}
|
|
126799
127183
|
const engineType = conduitConfiguration.engineConfig?.type ?? null;
|
|
126800
127184
|
const engineConfig = conduitConfiguration.engineConfig ?? null;
|
|
127185
|
+
const servedModelName = modelManager.resolvedServedModelName;
|
|
127186
|
+
const clientModelName = isPlainObject$a(body) && typeof body.model === "string" ? body.model : null;
|
|
126801
127187
|
const serializedBody = isPlainObject$a(body)
|
|
126802
|
-
? JSON.stringify(
|
|
127188
|
+
? JSON.stringify(servedModelName
|
|
127189
|
+
? {
|
|
127190
|
+
...body,
|
|
127191
|
+
model: servedModelName
|
|
127192
|
+
}
|
|
127193
|
+
: body)
|
|
126803
127194
|
: typeof body === "string"
|
|
126804
127195
|
? body
|
|
126805
127196
|
: JSON.stringify(body);
|
|
@@ -126917,7 +127308,8 @@ async function proxyEmbeddingsRoute({ body, conduitConfiguration, endpointId, lo
|
|
|
126917
127308
|
onComplete: onMonitoringComplete,
|
|
126918
127309
|
requestBodyBytes,
|
|
126919
127310
|
requestPath: "/v1/embeddings",
|
|
126920
|
-
requestStartedAt
|
|
127311
|
+
requestStartedAt,
|
|
127312
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
126921
127313
|
});
|
|
126922
127314
|
return {
|
|
126923
127315
|
body: monitoredResponse.stream,
|
|
@@ -126942,8 +127334,10 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
126942
127334
|
}
|
|
126943
127335
|
const engineType = conduitConfiguration.engineConfig?.type ?? null;
|
|
126944
127336
|
const engineConfig = conduitConfiguration.engineConfig ?? null;
|
|
127337
|
+
const servedModelName = modelManager.resolvedServedModelName;
|
|
127338
|
+
const clientModelName = isPlainObject$a(body) && typeof body.model === "string" ? body.model : null;
|
|
126945
127339
|
const effectiveBody = modelManager.model.multimodalEnabled ? body : stripImagesFromBody(body);
|
|
126946
|
-
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 });
|
|
126947
127341
|
const requestStartedAt = Date.now();
|
|
126948
127342
|
const requestBody = JSON.parse(serializedBody);
|
|
126949
127343
|
const streamRequested = requestBody.stream === true;
|
|
@@ -127083,7 +127477,8 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127083
127477
|
onComplete: onMonitoringComplete,
|
|
127084
127478
|
requestBodyBytes,
|
|
127085
127479
|
requestPath: path,
|
|
127086
|
-
requestStartedAt
|
|
127480
|
+
requestStartedAt,
|
|
127481
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127087
127482
|
})
|
|
127088
127483
|
: monitorEngineResponseSingle({
|
|
127089
127484
|
agentEngineType: engineType ?? "unknown",
|
|
@@ -127095,7 +127490,8 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127095
127490
|
onComplete: onMonitoringComplete,
|
|
127096
127491
|
requestBodyBytes,
|
|
127097
127492
|
requestPath: path,
|
|
127098
|
-
requestStartedAt
|
|
127493
|
+
requestStartedAt,
|
|
127494
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127099
127495
|
});
|
|
127100
127496
|
return {
|
|
127101
127497
|
body: monitoredResponse.stream,
|
|
@@ -158600,6 +158996,122 @@ async function detectDockerVersion() {
|
|
|
158600
158996
|
}
|
|
158601
158997
|
}
|
|
158602
158998
|
|
|
158999
|
+
/**
|
|
159000
|
+
* Flattens flat CLI extra-arg tokens into [arg, value] pairs, sorted by ARG NAME (ascending, ties by
|
|
159001
|
+
* value). `--flag=value` pairs split on the first `=`; a bare `--flag` consumes the following token
|
|
159002
|
+
* as its value when that token does not start with "-" (classic CLI convention); anything else
|
|
159003
|
+
* (flags, non-strings) is dropped. Secret-like option values are masked before pairing so they
|
|
159004
|
+
* never reach execution reports.
|
|
159005
|
+
*/
|
|
159006
|
+
function pairExtraArgs(tokens) {
|
|
159007
|
+
if (!Array.isArray(tokens)) {
|
|
159008
|
+
return [];
|
|
159009
|
+
}
|
|
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 : "")));
|
|
159013
|
+
const pairs = [];
|
|
159014
|
+
let index = 0;
|
|
159015
|
+
while (index < list.length) {
|
|
159016
|
+
const token = list[index];
|
|
159017
|
+
if (typeof token !== "string" || token.length === 0 || !token.startsWith("-")) {
|
|
159018
|
+
index++;
|
|
159019
|
+
continue;
|
|
159020
|
+
}
|
|
159021
|
+
const separator = token.indexOf("=");
|
|
159022
|
+
if (separator > -1) {
|
|
159023
|
+
const arg = token.slice(0, separator);
|
|
159024
|
+
if (arg.length > 0) {
|
|
159025
|
+
pairs.push([arg, token.slice(separator + 1)]);
|
|
159026
|
+
}
|
|
159027
|
+
index++;
|
|
159028
|
+
continue;
|
|
159029
|
+
}
|
|
159030
|
+
const next = list[index + 1];
|
|
159031
|
+
const consumesNext = typeof next === "string" && next.length > 0 && !next.startsWith("-");
|
|
159032
|
+
if (consumesNext) {
|
|
159033
|
+
pairs.push([token, next]);
|
|
159034
|
+
index += 2;
|
|
159035
|
+
}
|
|
159036
|
+
else {
|
|
159037
|
+
pairs.push([token, ""]);
|
|
159038
|
+
index++;
|
|
159039
|
+
}
|
|
159040
|
+
}
|
|
159041
|
+
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);
|
|
159042
|
+
}
|
|
159043
|
+
/**
|
|
159044
|
+
* Files AT MOST ONE engine_execution report per engine startup. `beginStartup(runAt)` re-arms the
|
|
159045
|
+
* latch on every fresh model start (initial boot or cycle) and stamps the startup epoch used as the
|
|
159046
|
+
* row's `run_at`. Whichever of the three report* paths fires first wins: startup failure, in-flight
|
|
159047
|
+
* crash, or first successful full prompt completion.
|
|
159048
|
+
*/
|
|
159049
|
+
class EngineExecutionReporter {
|
|
159050
|
+
options;
|
|
159051
|
+
currentStartupAt = null;
|
|
159052
|
+
reportedForCurrentStartup = false;
|
|
159053
|
+
constructor(options) {
|
|
159054
|
+
this.options = options;
|
|
159055
|
+
}
|
|
159056
|
+
/** Re-arms the latch; the stamp becomes the row's `run_at` (moment startup began). */
|
|
159057
|
+
beginStartup(runAt) {
|
|
159058
|
+
this.currentStartupAt = runAt;
|
|
159059
|
+
this.reportedForCurrentStartup = false;
|
|
159060
|
+
}
|
|
159061
|
+
/** Reports a startup failure (rejected prepare/start, readiness timeout, pre-ready death). */
|
|
159062
|
+
async reportStartupFailure(report) {
|
|
159063
|
+
await this.file(report, false);
|
|
159064
|
+
}
|
|
159065
|
+
/** Reports a spontaneous crash of an engine that had reached the running state. */
|
|
159066
|
+
async reportRuntimeCrash(report) {
|
|
159067
|
+
await this.file(report, false);
|
|
159068
|
+
}
|
|
159069
|
+
/** Reports the first fully-responded, token-bearing prompt completion since startup. */
|
|
159070
|
+
async reportSuccess(report) {
|
|
159071
|
+
await this.file(report, true);
|
|
159072
|
+
}
|
|
159073
|
+
async file(report, success) {
|
|
159074
|
+
if (this.reportedForCurrentStartup || this.currentStartupAt === null) {
|
|
159075
|
+
return;
|
|
159076
|
+
}
|
|
159077
|
+
// Latch BEFORE the network call: a thrown POST cannot double-file for this startup.
|
|
159078
|
+
this.reportedForCurrentStartup = true;
|
|
159079
|
+
const context = this.options.buildContext();
|
|
159080
|
+
const payload = {
|
|
159081
|
+
avgTps: report.throughput.avgTps,
|
|
159082
|
+
completionTokens: report.usage.completionTokens,
|
|
159083
|
+
durationMs: report.durationMs,
|
|
159084
|
+
engineType: context.engineType,
|
|
159085
|
+
engineVersion: context.engineVersion,
|
|
159086
|
+
errorDetail: success ? null : report.errorDetail,
|
|
159087
|
+
errorType: success ? null : report.errorType,
|
|
159088
|
+
extraArgs: context.extraArgsPairs,
|
|
159089
|
+
finishedAtISO: new Date().toISOString(),
|
|
159090
|
+
peakTps: report.throughput.peakTps,
|
|
159091
|
+
promptTokens: report.usage.promptTokens,
|
|
159092
|
+
runAtISO: this.currentStartupAt.toISOString(),
|
|
159093
|
+
success,
|
|
159094
|
+
ttftMs: report.ttftMs,
|
|
159095
|
+
totalTokens: report.usage.totalTokens
|
|
159096
|
+
};
|
|
159097
|
+
try {
|
|
159098
|
+
await this.options.report(payload);
|
|
159099
|
+
this.options.logger.info("Engine execution outcome reported", {
|
|
159100
|
+
inferenceSourceID: this.options.sourceLabel,
|
|
159101
|
+
success
|
|
159102
|
+
});
|
|
159103
|
+
}
|
|
159104
|
+
catch (error) {
|
|
159105
|
+
// Losing one report is preferable to filing two; the latch stays latched.
|
|
159106
|
+
this.options.logger.warn("Failed to report engine execution outcome", {
|
|
159107
|
+
error: asError(error),
|
|
159108
|
+
inferenceSourceID: this.options.sourceLabel,
|
|
159109
|
+
success
|
|
159110
|
+
});
|
|
159111
|
+
}
|
|
159112
|
+
}
|
|
159113
|
+
}
|
|
159114
|
+
|
|
158603
159115
|
async function createApplication({ abortController, apiClient, configuration, logger }) {
|
|
158604
159116
|
ensureDockerValidEnv();
|
|
158605
159117
|
logger.info("Fetching conduit configuration");
|
|
@@ -158631,6 +159143,87 @@ async function createApplication({ abortController, apiClient, configuration, lo
|
|
|
158631
159143
|
error: asError(error)
|
|
158632
159144
|
});
|
|
158633
159145
|
}
|
|
159146
|
+
const reporter = new EngineExecutionReporter({
|
|
159147
|
+
buildContext: () => {
|
|
159148
|
+
const engineType = conduitConfiguration.engineConfig?.type ?? "llama.cpp";
|
|
159149
|
+
const versions = {
|
|
159150
|
+
custom: null,
|
|
159151
|
+
exllamav3: machine?.exllamav3Version ?? null,
|
|
159152
|
+
"llama.cpp": machine?.llamaCppVersion ?? null,
|
|
159153
|
+
"mlx-lm": machine?.mlxlmVersion ?? null,
|
|
159154
|
+
sglang: machine?.sglangVersion ?? null,
|
|
159155
|
+
"tensorrt-llm": machine?.tensorrtLlmVersion ?? null,
|
|
159156
|
+
vllm: machine?.vllmVersion ?? null
|
|
159157
|
+
};
|
|
159158
|
+
return {
|
|
159159
|
+
engineType,
|
|
159160
|
+
engineVersion: versions[engineType] ?? null,
|
|
159161
|
+
extraArgsPairs: pairExtraArgs(conduitConfiguration.engineConfig?.extraArgs)
|
|
159162
|
+
};
|
|
159163
|
+
},
|
|
159164
|
+
logger,
|
|
159165
|
+
report: payload => apiClient.reportEngineExecution(payload),
|
|
159166
|
+
sourceLabel: configuration.inferenceSourceID
|
|
159167
|
+
});
|
|
159168
|
+
// Intercept the prompt-metrics chokepoint so the first fully-responded, token-bearing prompt of
|
|
159169
|
+
// each fresh startup files the one-shot engine_execution success report. Handlers close over the
|
|
159170
|
+
// SAME `apiClient` object and read `reportPromptMetrics` at request-dispatch time (which always
|
|
159171
|
+
// follows this point), so the wrapped method is what they invoke.
|
|
159172
|
+
const rawReportPromptMetrics = apiClient.reportPromptMetrics;
|
|
159173
|
+
apiClient.reportPromptMetrics = async (payload) => {
|
|
159174
|
+
if (payload.successful && payload.completionTokens > 0 && payload.latencyMs > 0) {
|
|
159175
|
+
// The one-shot report is kicked off and its settlement attached HERE (before any await): if the
|
|
159176
|
+
// metrics path throws below, the report promise must still be able to log its own rejection.
|
|
159177
|
+
const successReport = reporter
|
|
159178
|
+
.reportSuccess({
|
|
159179
|
+
durationMs: payload.latencyMs,
|
|
159180
|
+
errorDetail: null,
|
|
159181
|
+
errorType: null,
|
|
159182
|
+
throughput: {
|
|
159183
|
+
avgTps: payload.tokensPerSecond,
|
|
159184
|
+
peakTps: null
|
|
159185
|
+
},
|
|
159186
|
+
ttftMs: payload.timeToFirstTokenMs ?? 0,
|
|
159187
|
+
usage: {
|
|
159188
|
+
completionTokens: payload.completionTokens,
|
|
159189
|
+
promptTokens: payload.promptTokens,
|
|
159190
|
+
totalTokens: payload.totalTokens
|
|
159191
|
+
}
|
|
159192
|
+
})
|
|
159193
|
+
.catch(error => {
|
|
159194
|
+
logger.warn("Engine execution success report failed", {
|
|
159195
|
+
error: asError(error)
|
|
159196
|
+
});
|
|
159197
|
+
});
|
|
159198
|
+
await rawReportPromptMetrics(payload);
|
|
159199
|
+
await successReport;
|
|
159200
|
+
return;
|
|
159201
|
+
}
|
|
159202
|
+
await rawReportPromptMetrics(payload);
|
|
159203
|
+
};
|
|
159204
|
+
const SECRET_ARG_MASK_PATTERN = /(-{1,2}[A-Za-z0-9_.]*(?:api[-_]?key|hf[-_]?token|token)(?:\s+|[=:]))\S+/gi;
|
|
159205
|
+
// Assembles the payload shared by the startup-failure and runtime-crash report paths. Stderr
|
|
159206
|
+
// may echo secrets, so mask `--api-key`/token-looking args before they reach the DB.
|
|
159207
|
+
function buildCrashReport(error, exitCode, signal) {
|
|
159208
|
+
const classification = classifyEngineFailure({ error, exitCode, signal });
|
|
159209
|
+
const raw = normalizeEngineError(error.message);
|
|
159210
|
+
const masked = raw.replace(SECRET_ARG_MASK_PATTERN, "$1***");
|
|
159211
|
+
return {
|
|
159212
|
+
durationMs: 0,
|
|
159213
|
+
errorDetail: masked.slice(0, 2048),
|
|
159214
|
+
errorType: classification,
|
|
159215
|
+
ttftMs: 0,
|
|
159216
|
+
throughput: {
|
|
159217
|
+
avgTps: 0,
|
|
159218
|
+
peakTps: null
|
|
159219
|
+
},
|
|
159220
|
+
usage: {
|
|
159221
|
+
completionTokens: 0,
|
|
159222
|
+
promptTokens: 0,
|
|
159223
|
+
totalTokens: 0
|
|
159224
|
+
}
|
|
159225
|
+
};
|
|
159226
|
+
}
|
|
158634
159227
|
const conduitStateManager = new ConduitStateManager({
|
|
158635
159228
|
initialState: {
|
|
158636
159229
|
state: "initialising"
|
|
@@ -158682,6 +159275,17 @@ async function createApplication({ abortController, apiClient, configuration, lo
|
|
|
158682
159275
|
});
|
|
158683
159276
|
stopRequestedByControl = false;
|
|
158684
159277
|
setErrorState({ error: normalizeEngineError(err.message) });
|
|
159278
|
+
// Spontaneous death of a SERVING engine → crash report, suppressed by the latch if the
|
|
159279
|
+
// startup's one-shot outcome was already filed. Startup-path failures report from
|
|
159280
|
+
// `startEngine`'s catch; this listener is the RUNTIME-crash path only.
|
|
159281
|
+
if (modelManager.wasRunning && !err.message.includes("interrupted by stop request")) {
|
|
159282
|
+
const crashReport = buildCrashReport(err, modelManager.lastExitCode, modelManager.lastExitSignal);
|
|
159283
|
+
reporter.reportRuntimeCrash(crashReport).catch(crashReportError => {
|
|
159284
|
+
logger.warn("Engine execution crash report failed", {
|
|
159285
|
+
error: asError(crashReportError)
|
|
159286
|
+
});
|
|
159287
|
+
});
|
|
159288
|
+
}
|
|
158685
159289
|
});
|
|
158686
159290
|
modelManager.on("engineReady", () => {
|
|
158687
159291
|
setOnlineState();
|
|
@@ -158741,24 +159345,40 @@ async function createApplication({ abortController, apiClient, configuration, lo
|
|
|
158741
159345
|
};
|
|
158742
159346
|
async function startEngine() {
|
|
158743
159347
|
logger.info("Engine start requested");
|
|
158744
|
-
|
|
158745
|
-
|
|
158746
|
-
|
|
158747
|
-
|
|
158748
|
-
|
|
158749
|
-
|
|
158750
|
-
|
|
159348
|
+
reporter.beginStartup(new Date());
|
|
159349
|
+
try {
|
|
159350
|
+
conduitStateManager.setState({
|
|
159351
|
+
modelFileName,
|
|
159352
|
+
modelName,
|
|
159353
|
+
state: "downloadingModelFiles",
|
|
159354
|
+
totalProgress: {
|
|
159355
|
+
file: 0,
|
|
159356
|
+
total: 0
|
|
159357
|
+
}
|
|
159358
|
+
});
|
|
159359
|
+
await conduitStateReportManager.reportNow();
|
|
159360
|
+
await modelManager.prepare({
|
|
159361
|
+
onDownloadProgress: reportDownloadProgress
|
|
159362
|
+
});
|
|
159363
|
+
conduitStateManager.setState({
|
|
159364
|
+
state: "bootingEngine"
|
|
159365
|
+
});
|
|
159366
|
+
await conduitStateReportManager.reportNow();
|
|
159367
|
+
await modelManager.start();
|
|
159368
|
+
}
|
|
159369
|
+
catch (error) {
|
|
159370
|
+
const parsedError = asError(error);
|
|
159371
|
+
// Operator-initiated aborts are not startup failures worth reporting.
|
|
159372
|
+
if (!parsedError.message.includes("interrupted by stop request")) {
|
|
159373
|
+
const startupReport = buildCrashReport(parsedError, modelManager.lastExitCode, modelManager.lastExitSignal);
|
|
159374
|
+
reporter.reportStartupFailure(startupReport).catch(startupReportError => {
|
|
159375
|
+
logger.warn("Engine execution startup report failed", {
|
|
159376
|
+
error: asError(startupReportError)
|
|
159377
|
+
});
|
|
159378
|
+
});
|
|
158751
159379
|
}
|
|
158752
|
-
|
|
158753
|
-
|
|
158754
|
-
await modelManager.prepare({
|
|
158755
|
-
onDownloadProgress: reportDownloadProgress
|
|
158756
|
-
});
|
|
158757
|
-
conduitStateManager.setState({
|
|
158758
|
-
state: "bootingEngine"
|
|
158759
|
-
});
|
|
158760
|
-
await conduitStateReportManager.reportNow();
|
|
158761
|
-
await modelManager.start();
|
|
159380
|
+
throw error;
|
|
159381
|
+
}
|
|
158762
159382
|
}
|
|
158763
159383
|
async function stopEngine({ reason }) {
|
|
158764
159384
|
if (!modelManager.canStop) {
|
|
@@ -159014,7 +159634,11 @@ function createModelManagerFromConfig(conduitConfiguration, configuration, logge
|
|
|
159014
159634
|
return new ModelManager({
|
|
159015
159635
|
contextLength: conduitConfiguration.contextLength ?? null,
|
|
159016
159636
|
engineConfig: engineConfig
|
|
159017
|
-
? {
|
|
159637
|
+
? {
|
|
159638
|
+
baseUrl: engineConfig.baseUrl ?? null,
|
|
159639
|
+
extraArgs: engineConfig.extraArgs,
|
|
159640
|
+
type: engineConfig.type
|
|
159641
|
+
}
|
|
159018
159642
|
: null,
|
|
159019
159643
|
enginePort: configuration.enginePort,
|
|
159020
159644
|
engineType: engineConfig?.type ?? "llama.cpp",
|
|
@@ -159025,7 +159649,11 @@ function createModelManagerFromConfig(conduitConfiguration, configuration, logge
|
|
|
159025
159649
|
}
|
|
159026
159650
|
function getConduitModelFileName(configuration) {
|
|
159027
159651
|
const { source } = configuration.targetModel;
|
|
159028
|
-
|
|
159652
|
+
if (source.type === "huggingface")
|
|
159653
|
+
return source.slug;
|
|
159654
|
+
if (source.type === "storage")
|
|
159655
|
+
return source.irid;
|
|
159656
|
+
return configuration.targetModel.id;
|
|
159029
159657
|
}
|
|
159030
159658
|
function getConduitModelName(configuration) {
|
|
159031
159659
|
return configuration.targetModel.id;
|