@infersec/conduit 1.113.0 → 1.114.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +447 -95
- package/dist/cli.sea.cjs +447 -95
- package/dist/commands/engineOptions.d.ts +1 -0
- package/dist/modelManagement/ModelManager.d.ts +8 -0
- package/dist/reporting/engineExecutionReporter.d.ts +2 -1
- package/dist/utils/engineMetrics.d.ts +3 -2
- package/dist/utils/openai.d.ts +14 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -19886,6 +19886,7 @@ object$5({
|
|
|
19886
19886
|
});
|
|
19887
19887
|
|
|
19888
19888
|
const LLMEngineSchema = _enum$1([
|
|
19889
|
+
"custom",
|
|
19889
19890
|
"exllamav3",
|
|
19890
19891
|
"llama.cpp",
|
|
19891
19892
|
"mlx-lm",
|
|
@@ -19894,8 +19895,25 @@ const LLMEngineSchema = _enum$1([
|
|
|
19894
19895
|
"vllm"
|
|
19895
19896
|
]);
|
|
19896
19897
|
const EngineConfigSchema = object$5({
|
|
19898
|
+
baseUrl: string$2().url().nullable().default(null),
|
|
19897
19899
|
extraArgs: array$1(string$2()),
|
|
19898
19900
|
type: LLMEngineSchema
|
|
19901
|
+
})
|
|
19902
|
+
.superRefine((config, ctx) => {
|
|
19903
|
+
if (config.type === "custom" && config.baseUrl === null) {
|
|
19904
|
+
ctx.addIssue({
|
|
19905
|
+
code: "custom",
|
|
19906
|
+
message: "Engine type 'custom' requires a base URL",
|
|
19907
|
+
path: ["baseUrl"]
|
|
19908
|
+
});
|
|
19909
|
+
}
|
|
19910
|
+
if (config.type !== "custom" && config.baseUrl !== null) {
|
|
19911
|
+
ctx.addIssue({
|
|
19912
|
+
code: "custom",
|
|
19913
|
+
message: "Base URL is only valid for engine type 'custom'",
|
|
19914
|
+
path: ["baseUrl"]
|
|
19915
|
+
});
|
|
19916
|
+
}
|
|
19899
19917
|
});
|
|
19900
19918
|
const LLMModelFormatSchema = _enum$1([
|
|
19901
19919
|
// VLLM / SGLang / TensorRT-LLM
|
|
@@ -19954,14 +19972,18 @@ const LLMModelSchema = object$5({
|
|
|
19954
19972
|
id: string$2().min(1),
|
|
19955
19973
|
multimodalEnabled: boolean$1(),
|
|
19956
19974
|
source: discriminatedUnion("type", [
|
|
19975
|
+
// Custom engines: no local model record, serving is external
|
|
19957
19976
|
object$5({
|
|
19958
|
-
|
|
19959
|
-
type: literal("storage")
|
|
19977
|
+
type: literal("external")
|
|
19960
19978
|
}),
|
|
19961
19979
|
object$5({
|
|
19962
19980
|
modelSecret: string$2().min(1).nullable(),
|
|
19963
19981
|
slug: string$2().min(1),
|
|
19964
19982
|
type: literal("huggingface")
|
|
19983
|
+
}),
|
|
19984
|
+
object$5({
|
|
19985
|
+
irid: IRIDSchema,
|
|
19986
|
+
type: literal("storage")
|
|
19965
19987
|
})
|
|
19966
19988
|
]),
|
|
19967
19989
|
taskType: LLMModelTaskTypeSchema,
|
|
@@ -21105,7 +21127,8 @@ const CreateModelResponseSchema = object$5({
|
|
|
21105
21127
|
const CreateSourceBodySchema = object$5({
|
|
21106
21128
|
contextLength: number$1().int().positive().max(1048576).optional(),
|
|
21107
21129
|
engineId: ULIDSchema,
|
|
21108
|
-
|
|
21130
|
+
// Optional for custom engines, which serve externally managed models
|
|
21131
|
+
modelID: ULIDSchema.nullable().optional(),
|
|
21109
21132
|
name: ResourceNameSchema,
|
|
21110
21133
|
quantizationLabel: string$2().min(1).max(128).optional()
|
|
21111
21134
|
});
|
|
@@ -21154,7 +21177,7 @@ const SourceDetailResponseSchema = object$5({
|
|
|
21154
21177
|
const UpdateSourceBodySchema = object$5({
|
|
21155
21178
|
contextLength: number$1().int().positive().nullable().optional(),
|
|
21156
21179
|
engineId: ULIDSchema.nullable().optional(),
|
|
21157
|
-
modelID: ULIDSchema.optional(),
|
|
21180
|
+
modelID: ULIDSchema.nullable().optional(),
|
|
21158
21181
|
name: ResourceNameSchema.optional(),
|
|
21159
21182
|
quantizationLabel: string$2().min(1).max(128).nullable().optional()
|
|
21160
21183
|
});
|
|
@@ -21278,6 +21301,7 @@ const CreateEndpointResponseSchema = object$5({
|
|
|
21278
21301
|
id: ULIDSchema
|
|
21279
21302
|
});
|
|
21280
21303
|
const EngineOutputSchema = object$5({
|
|
21304
|
+
baseUrl: string$2().nullable(),
|
|
21281
21305
|
created: string$2(),
|
|
21282
21306
|
extraArgs: array$1(string$2()),
|
|
21283
21307
|
id: ULIDSchema,
|
|
@@ -21286,11 +21310,29 @@ const EngineOutputSchema = object$5({
|
|
|
21286
21310
|
updated: string$2()
|
|
21287
21311
|
});
|
|
21288
21312
|
const CreateEngineBodySchema = object$5({
|
|
21313
|
+
baseUrl: string$2().url().nullable().optional(),
|
|
21289
21314
|
extraArgs: array$1(string$2()).optional(),
|
|
21290
21315
|
name: ResourceNameSchema,
|
|
21291
21316
|
type: LLMEngineSchema
|
|
21317
|
+
})
|
|
21318
|
+
.superRefine((body, ctx) => {
|
|
21319
|
+
if (body.type === "custom" && !body.baseUrl) {
|
|
21320
|
+
ctx.addIssue({
|
|
21321
|
+
code: "custom",
|
|
21322
|
+
message: "Engine type 'custom' requires a base URL",
|
|
21323
|
+
path: ["baseUrl"]
|
|
21324
|
+
});
|
|
21325
|
+
}
|
|
21326
|
+
if (body.type !== "custom" && body.baseUrl) {
|
|
21327
|
+
ctx.addIssue({
|
|
21328
|
+
code: "custom",
|
|
21329
|
+
message: "Base URL is only valid for engine type 'custom'",
|
|
21330
|
+
path: ["baseUrl"]
|
|
21331
|
+
});
|
|
21332
|
+
}
|
|
21292
21333
|
});
|
|
21293
21334
|
const UpdateEngineBodySchema = object$5({
|
|
21335
|
+
baseUrl: string$2().url().nullable().optional(),
|
|
21294
21336
|
extraArgs: array$1(string$2()).optional(),
|
|
21295
21337
|
name: ResourceNameSchema.optional(),
|
|
21296
21338
|
type: LLMEngineSchema.optional()
|
|
@@ -22096,6 +22138,11 @@ const RecommendedModelSchema = object$5({
|
|
|
22096
22138
|
const recommendedModels = RecommendedModelSchema.array().parse(modelsData);
|
|
22097
22139
|
|
|
22098
22140
|
const ENGINE_API_COMPATIBILITY = {
|
|
22141
|
+
custom: {
|
|
22142
|
+
nativeAnthropicMessages: false,
|
|
22143
|
+
supportsEmbeddings: true,
|
|
22144
|
+
supportsVision: true
|
|
22145
|
+
},
|
|
22099
22146
|
exllamav3: {
|
|
22100
22147
|
nativeAnthropicMessages: false,
|
|
22101
22148
|
supportsEmbeddings: false,
|
|
@@ -111745,6 +111792,27 @@ function registerEndpointCommands({ program }) {
|
|
|
111745
111792
|
}
|
|
111746
111793
|
|
|
111747
111794
|
const ENGINE_TYPES = LLMEngineSchema.options;
|
|
111795
|
+
function isValidURL(value) {
|
|
111796
|
+
try {
|
|
111797
|
+
const url = new URL(value);
|
|
111798
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
111799
|
+
}
|
|
111800
|
+
catch (_error) {
|
|
111801
|
+
return false;
|
|
111802
|
+
}
|
|
111803
|
+
}
|
|
111804
|
+
function validateEngineBaseURL({ baseUrl, type }) {
|
|
111805
|
+
if (type === "custom" && !baseUrl) {
|
|
111806
|
+
throw new Error("Engine type 'custom' requires --base-url");
|
|
111807
|
+
}
|
|
111808
|
+
if (baseUrl !== undefined && type !== "custom") {
|
|
111809
|
+
throw new Error("--base-url is only valid for engine type 'custom'");
|
|
111810
|
+
}
|
|
111811
|
+
if (baseUrl !== undefined && baseUrl !== "" && !isValidURL(baseUrl)) {
|
|
111812
|
+
throw new Error(`Invalid --base-url value: ${baseUrl}`);
|
|
111813
|
+
}
|
|
111814
|
+
return baseUrl === "" ? null : (baseUrl ?? null);
|
|
111815
|
+
}
|
|
111748
111816
|
function buildEngineCreateBody(options) {
|
|
111749
111817
|
if (!options.name) {
|
|
111750
111818
|
throw new Error("--name is required");
|
|
@@ -111755,7 +111823,12 @@ function buildEngineCreateBody(options) {
|
|
|
111755
111823
|
if (!ENGINE_TYPES.includes(options.type)) {
|
|
111756
111824
|
throw new Error(`Invalid engine type: ${options.type} (expected one of: ${ENGINE_TYPES.join(", ")})`);
|
|
111757
111825
|
}
|
|
111826
|
+
const baseUrl = validateEngineBaseURL({
|
|
111827
|
+
baseUrl: options.baseUrl,
|
|
111828
|
+
type: options.type
|
|
111829
|
+
});
|
|
111758
111830
|
return {
|
|
111831
|
+
baseUrl,
|
|
111759
111832
|
extraArgs: options.arg ?? [],
|
|
111760
111833
|
name: options.name,
|
|
111761
111834
|
type: options.type
|
|
@@ -111773,6 +111846,24 @@ function buildEngineUpdateBody(options) {
|
|
|
111773
111846
|
}
|
|
111774
111847
|
if (options.arg !== undefined)
|
|
111775
111848
|
body.extraArgs = options.arg;
|
|
111849
|
+
if (options.baseUrl !== undefined) {
|
|
111850
|
+
const targetType = body.type;
|
|
111851
|
+
if (targetType !== undefined) {
|
|
111852
|
+
body.baseUrl = validateEngineBaseURL({
|
|
111853
|
+
baseUrl: options.baseUrl,
|
|
111854
|
+
type: targetType
|
|
111855
|
+
});
|
|
111856
|
+
}
|
|
111857
|
+
else if (options.baseUrl === "") {
|
|
111858
|
+
body.baseUrl = null;
|
|
111859
|
+
}
|
|
111860
|
+
else if (!isValidURL(options.baseUrl)) {
|
|
111861
|
+
throw new Error(`Invalid --base-url value: ${options.baseUrl}`);
|
|
111862
|
+
}
|
|
111863
|
+
else {
|
|
111864
|
+
body.baseUrl = options.baseUrl;
|
|
111865
|
+
}
|
|
111866
|
+
}
|
|
111776
111867
|
return body;
|
|
111777
111868
|
}
|
|
111778
111869
|
|
|
@@ -111786,10 +111877,11 @@ function registerEngineCommands({ program }) {
|
|
|
111786
111877
|
.description("Create or update an inference engine resource")
|
|
111787
111878
|
.option("--api-url <url>", "API base URL (required, no environment variable fallback)")
|
|
111788
111879
|
.option("--arg <flag>", 'Raw engine CLI flag, repeatable (eg --arg "--flash-attn on")', collect, [])
|
|
111880
|
+
.option("--base-url <url>", "Base URL of an external OpenAI-compatible server (engine type 'custom' only)")
|
|
111789
111881
|
.option("--id <ulid>", "Target an existing engine by ID (requires --update)")
|
|
111790
111882
|
.option("--key <value>", "API key (required, no environment variable fallback)")
|
|
111791
111883
|
.option("--name <name>", "Engine name (matched by --update)")
|
|
111792
|
-
.option("--type <type>", "Engine type: llama.cpp|vllm|sglang|tensorrt-llm|mlx-lm|exllamav3")
|
|
111884
|
+
.option("--type <type>", "Engine type: llama.cpp|vllm|sglang|tensorrt-llm|mlx-lm|exllamav3|custom")
|
|
111793
111885
|
.option("--update", "Update an existing engine matched by name (or --id) instead of erroring")
|
|
111794
111886
|
.action(async (options) => {
|
|
111795
111887
|
const { apiURL, apiKey } = resolveManagementConnection(options);
|
|
@@ -117789,6 +117881,9 @@ async function getChatTemplateEngineArgs({ engine, model, targetDirectory }) {
|
|
|
117789
117881
|
return [];
|
|
117790
117882
|
const flag = FLAG_BASED_ENGINE_ARGS[engine];
|
|
117791
117883
|
if (!flag) {
|
|
117884
|
+
if (engine === "custom") {
|
|
117885
|
+
console.warn("[chatTemplate] Custom engines manage their own serving; ignoring chat template override");
|
|
117886
|
+
}
|
|
117792
117887
|
if (engine === "tensorrt-llm") {
|
|
117793
117888
|
console.warn("[chatTemplate] TensorRT-LLM does not support chat template overrides; ignoring");
|
|
117794
117889
|
}
|
|
@@ -124686,7 +124781,7 @@ function matchesQuantizationVariant({ filePath, variant }) {
|
|
|
124686
124781
|
return segments.slice(0, -1).some(segment => matcher.test(segment));
|
|
124687
124782
|
}
|
|
124688
124783
|
async function findQuantizedModelTarget({ model, path }) {
|
|
124689
|
-
if (model.source.type
|
|
124784
|
+
if (model.source.type !== "huggingface") {
|
|
124690
124785
|
throw new Error("Model storage not supported yet");
|
|
124691
124786
|
}
|
|
124692
124787
|
if (model.format !== "gguf") {
|
|
@@ -125601,7 +125696,11 @@ function sanitizeSegment(value) {
|
|
|
125601
125696
|
.replace(new RegExp(`${SEPARATOR}{2,}`, "g"), SEPARATOR);
|
|
125602
125697
|
}
|
|
125603
125698
|
function createModelStorageKey(model) {
|
|
125604
|
-
const identifier = model.source.type === "huggingface"
|
|
125699
|
+
const identifier = model.source.type === "huggingface"
|
|
125700
|
+
? model.source.slug
|
|
125701
|
+
: model.source.type === "storage"
|
|
125702
|
+
? model.source.irid
|
|
125703
|
+
: model.id;
|
|
125605
125704
|
return `${model.source.type}${SEPARATOR}${sanitizeSegment(identifier)}`;
|
|
125606
125705
|
}
|
|
125607
125706
|
|
|
@@ -125677,8 +125776,11 @@ class ModelManager extends EventEmitter {
|
|
|
125677
125776
|
uniqueName;
|
|
125678
125777
|
contextLength;
|
|
125679
125778
|
logger;
|
|
125779
|
+
discoveredModelNames = [];
|
|
125780
|
+
customProbeCount = 0;
|
|
125680
125781
|
engineProcess = null;
|
|
125681
125782
|
healthPollInterval = null;
|
|
125783
|
+
lastCustomReadinessReport = null;
|
|
125682
125784
|
lastEngineError = null;
|
|
125683
125785
|
lifecycleState = "stopped";
|
|
125684
125786
|
downloadLockHandle = null;
|
|
@@ -125709,6 +125811,7 @@ class ModelManager extends EventEmitter {
|
|
|
125709
125811
|
}
|
|
125710
125812
|
async fetchOpenAI(path, opts) {
|
|
125711
125813
|
switch (this.engine) {
|
|
125814
|
+
case "custom":
|
|
125712
125815
|
case "exllamav3":
|
|
125713
125816
|
case "llama.cpp":
|
|
125714
125817
|
case "mlx-lm":
|
|
@@ -125716,6 +125819,9 @@ class ModelManager extends EventEmitter {
|
|
|
125716
125819
|
case "tensorrt-llm":
|
|
125717
125820
|
case "vllm": {
|
|
125718
125821
|
this.logger.debug(`Fetching from engine: ${path}`);
|
|
125822
|
+
const baseURL = this.engine === "custom"
|
|
125823
|
+
? this.requireCustomBaseURL()
|
|
125824
|
+
: `http://localhost:${this.enginePort}`;
|
|
125719
125825
|
const callerSignal = opts?.signal;
|
|
125720
125826
|
const controller = new AbortController();
|
|
125721
125827
|
const timeout = setTimeout(() => {
|
|
@@ -125726,7 +125832,7 @@ class ModelManager extends EventEmitter {
|
|
|
125726
125832
|
: controller.signal;
|
|
125727
125833
|
try {
|
|
125728
125834
|
const fetchStartedAt = Date.now();
|
|
125729
|
-
const response = await undiciExports.fetch(joinURL(
|
|
125835
|
+
const response = await undiciExports.fetch(joinURL(baseURL, path), {
|
|
125730
125836
|
...opts,
|
|
125731
125837
|
dispatcher: ENGINE_AGENT,
|
|
125732
125838
|
headers: {
|
|
@@ -125761,6 +125867,11 @@ class ModelManager extends EventEmitter {
|
|
|
125761
125867
|
modelID: this.model.id
|
|
125762
125868
|
});
|
|
125763
125869
|
switch (this.engine) {
|
|
125870
|
+
case "custom":
|
|
125871
|
+
if (this.model.chatTemplate) {
|
|
125872
|
+
this.logger.warn("Chat template overrides are ignored for custom engines: the remote server manages its own serving");
|
|
125873
|
+
}
|
|
125874
|
+
break;
|
|
125764
125875
|
case "exllamav3":
|
|
125765
125876
|
case "llama.cpp":
|
|
125766
125877
|
case "mlx-lm":
|
|
@@ -125816,7 +125927,9 @@ class ModelManager extends EventEmitter {
|
|
|
125816
125927
|
});
|
|
125817
125928
|
try {
|
|
125818
125929
|
this.engineProcess = await this.startEngineProcess();
|
|
125819
|
-
|
|
125930
|
+
if (this.engineProcess) {
|
|
125931
|
+
this.bindEngineProcessEvents(this.engineProcess);
|
|
125932
|
+
}
|
|
125820
125933
|
this.logger.info("Started LLM engine", {
|
|
125821
125934
|
agentEngineType: this.engine
|
|
125822
125935
|
});
|
|
@@ -125835,7 +125948,7 @@ class ModelManager extends EventEmitter {
|
|
|
125835
125948
|
if (!alreadyEmitted) {
|
|
125836
125949
|
this.emit("engineError", err);
|
|
125837
125950
|
}
|
|
125838
|
-
if (this.engineProcess) {
|
|
125951
|
+
if (this.engineProcess || this.engine === "custom") {
|
|
125839
125952
|
this.startHealthPoll();
|
|
125840
125953
|
}
|
|
125841
125954
|
throw err;
|
|
@@ -125843,6 +125956,9 @@ class ModelManager extends EventEmitter {
|
|
|
125843
125956
|
this.lifecycleState = "running";
|
|
125844
125957
|
this.reachedRunningState = true;
|
|
125845
125958
|
this.emit("engineReady");
|
|
125959
|
+
if (this.engine === "custom") {
|
|
125960
|
+
this.startHealthPoll();
|
|
125961
|
+
}
|
|
125846
125962
|
}
|
|
125847
125963
|
async stop() {
|
|
125848
125964
|
if (this.lifecycleState === "stopping") {
|
|
@@ -125863,6 +125979,8 @@ class ModelManager extends EventEmitter {
|
|
|
125863
125979
|
this.clearHealthPoll();
|
|
125864
125980
|
const processManager = this.engineProcess;
|
|
125865
125981
|
if (!processManager) {
|
|
125982
|
+
this.stopRequested = true;
|
|
125983
|
+
this.reachedRunningState = false;
|
|
125866
125984
|
this.lifecycleState = "stopped";
|
|
125867
125985
|
return;
|
|
125868
125986
|
}
|
|
@@ -125888,11 +126006,27 @@ class ModelManager extends EventEmitter {
|
|
|
125888
126006
|
get state() {
|
|
125889
126007
|
return this.lifecycleState;
|
|
125890
126008
|
}
|
|
126009
|
+
get resolvedServedModelName() {
|
|
126010
|
+
if (this.engine !== "custom")
|
|
126011
|
+
return null;
|
|
126012
|
+
return this.discoveredModelNames[0] ?? null;
|
|
126013
|
+
}
|
|
125891
126014
|
get wasRunning() {
|
|
125892
126015
|
return this.reachedRunningState;
|
|
125893
126016
|
}
|
|
126017
|
+
get customBaseURL() {
|
|
126018
|
+
if (this.engine !== "custom")
|
|
126019
|
+
return null;
|
|
126020
|
+
const baseUrl = this.engineConfig?.baseUrl;
|
|
126021
|
+
if (typeof baseUrl !== "string" || baseUrl.length === 0)
|
|
126022
|
+
return null;
|
|
126023
|
+
return baseUrl.replace(/\/+$/, "");
|
|
126024
|
+
}
|
|
125894
126025
|
async checkEngineReadiness() {
|
|
125895
126026
|
switch (this.engine) {
|
|
126027
|
+
case "custom": {
|
|
126028
|
+
return this.checkCustomReadiness();
|
|
126029
|
+
}
|
|
125896
126030
|
case "llama.cpp": {
|
|
125897
126031
|
return this.checkLlamacppReadiness();
|
|
125898
126032
|
}
|
|
@@ -125909,6 +126043,98 @@ class ModelManager extends EventEmitter {
|
|
|
125909
126043
|
return "ready";
|
|
125910
126044
|
}
|
|
125911
126045
|
}
|
|
126046
|
+
async checkCustomReadiness() {
|
|
126047
|
+
const baseURL = this.customBaseURL;
|
|
126048
|
+
if (!baseURL) {
|
|
126049
|
+
this.reportCustomReadinessChange("unreachable", "", "no base URL configured");
|
|
126050
|
+
return "unreachable";
|
|
126051
|
+
}
|
|
126052
|
+
const probeURL = joinURL(baseURL, "/v1/models");
|
|
126053
|
+
this.customProbeCount++;
|
|
126054
|
+
try {
|
|
126055
|
+
const response = await undiciExports.fetch(probeURL, {
|
|
126056
|
+
method: "GET",
|
|
126057
|
+
signal: AbortSignal.timeout(5000)
|
|
126058
|
+
});
|
|
126059
|
+
if (response.status === 503) {
|
|
126060
|
+
this.logger.debug("Custom engine probe: server loading", {
|
|
126061
|
+
attempt: this.customProbeCount,
|
|
126062
|
+
probeURL
|
|
126063
|
+
});
|
|
126064
|
+
this.reportCustomReadinessChange("loading", baseURL, "server loading (HTTP 503)");
|
|
126065
|
+
return "loading";
|
|
126066
|
+
}
|
|
126067
|
+
if (!response.ok) {
|
|
126068
|
+
const reason = `HTTP ${response.status} from /v1/models`;
|
|
126069
|
+
this.logger.debug("Custom engine probe: not ready", {
|
|
126070
|
+
attempt: this.customProbeCount,
|
|
126071
|
+
probeURL,
|
|
126072
|
+
reason
|
|
126073
|
+
});
|
|
126074
|
+
this.reportCustomReadinessChange("unreachable", baseURL, reason);
|
|
126075
|
+
return "unreachable";
|
|
126076
|
+
}
|
|
126077
|
+
const payload = (await response.json());
|
|
126078
|
+
const models = Array.isArray(payload.data) ? payload.data : [];
|
|
126079
|
+
const modelIDs = models
|
|
126080
|
+
.map(model => {
|
|
126081
|
+
if (model === null || typeof model !== "object")
|
|
126082
|
+
return null;
|
|
126083
|
+
const id = model.id;
|
|
126084
|
+
return typeof id === "string" ? id : null;
|
|
126085
|
+
})
|
|
126086
|
+
.filter((id) => id !== null);
|
|
126087
|
+
if (modelIDs.length === 0) {
|
|
126088
|
+
this.logger.debug("Custom engine probe: no models exposed yet", {
|
|
126089
|
+
attempt: this.customProbeCount,
|
|
126090
|
+
probeURL
|
|
126091
|
+
});
|
|
126092
|
+
this.reportCustomReadinessChange("loading", baseURL, "/v1/models returned no models");
|
|
126093
|
+
return "loading";
|
|
126094
|
+
}
|
|
126095
|
+
if (modelIDs.join("\n") !== this.discoveredModelNames.join("\n")) {
|
|
126096
|
+
this.discoveredModelNames = modelIDs;
|
|
126097
|
+
this.logger.info("Discovered models on custom engine endpoint", {
|
|
126098
|
+
engineBaseURL: baseURL,
|
|
126099
|
+
models: modelIDs,
|
|
126100
|
+
selectedModel: modelIDs[0]
|
|
126101
|
+
});
|
|
126102
|
+
}
|
|
126103
|
+
return "ready";
|
|
126104
|
+
}
|
|
126105
|
+
catch (error) {
|
|
126106
|
+
const reason = asError(error).message;
|
|
126107
|
+
this.logger.debug("Custom engine probe: request failed", {
|
|
126108
|
+
attempt: this.customProbeCount,
|
|
126109
|
+
probeURL,
|
|
126110
|
+
reason
|
|
126111
|
+
});
|
|
126112
|
+
this.reportCustomReadinessChange("unreachable", baseURL, reason);
|
|
126113
|
+
return "unreachable";
|
|
126114
|
+
}
|
|
126115
|
+
}
|
|
126116
|
+
reportCustomReadinessChange(readiness, baseURL, reason) {
|
|
126117
|
+
const key = `${readiness}:${reason}`;
|
|
126118
|
+
if (key === this.lastCustomReadinessReport) {
|
|
126119
|
+
// Same outcome as the last report: surface a heartbeat every 15
|
|
126120
|
+
// attempts (~30s while booting) so progress stays visible.
|
|
126121
|
+
if (this.customProbeCount % 15 === 0) {
|
|
126122
|
+
this.logger.info("Still waiting for custom engine endpoint", {
|
|
126123
|
+
attempt: this.customProbeCount,
|
|
126124
|
+
engineBaseURL: baseURL,
|
|
126125
|
+
reason
|
|
126126
|
+
});
|
|
126127
|
+
}
|
|
126128
|
+
return;
|
|
126129
|
+
}
|
|
126130
|
+
this.lastCustomReadinessReport = key;
|
|
126131
|
+
this.logger.warn(readiness === "loading"
|
|
126132
|
+
? "Custom engine endpoint is loading — waiting for /v1/models to expose a model"
|
|
126133
|
+
: "Custom engine endpoint unreachable — verify the server is running and reachable from conduit", {
|
|
126134
|
+
engineBaseURL: baseURL,
|
|
126135
|
+
reason
|
|
126136
|
+
});
|
|
126137
|
+
}
|
|
125912
126138
|
async checkGenericHealthReadiness() {
|
|
125913
126139
|
try {
|
|
125914
126140
|
const response = await undiciExports.fetch(joinURL(`http://localhost:${this.enginePort}`, "/health"), {
|
|
@@ -125964,23 +126190,41 @@ class ModelManager extends EventEmitter {
|
|
|
125964
126190
|
}
|
|
125965
126191
|
}
|
|
125966
126192
|
async waitForEngineReady() {
|
|
125967
|
-
const maxWaitMs = 15 * 60 * 1000;
|
|
126193
|
+
const maxWaitMs = Number.parseInt(process.env.ENGINE_STARTUP_TIMEOUT_MS ?? "", 10) || 15 * 60 * 1000;
|
|
125968
126194
|
const pollIntervalMs = 2000;
|
|
125969
126195
|
const start = Date.now();
|
|
126196
|
+
if (this.engine === "custom") {
|
|
126197
|
+
this.logger.info(`Connecting to external OpenAI-compatible server at ${this.customBaseURL ?? "(no base URL configured)"} — serving begins once /v1/models exposes a model`, {
|
|
126198
|
+
engineBaseURL: this.customBaseURL ?? ""
|
|
126199
|
+
});
|
|
126200
|
+
}
|
|
125970
126201
|
while (Date.now() - start < maxWaitMs) {
|
|
125971
|
-
if (this.lifecycleState === "stopping") {
|
|
126202
|
+
if (this.lifecycleState === "stopping" || this.stopRequested) {
|
|
125972
126203
|
throw new Error("LLM engine startup interrupted by stop request");
|
|
125973
126204
|
}
|
|
125974
|
-
if (!this.engineProcess) {
|
|
126205
|
+
if (!this.engineProcess && this.engine !== "custom") {
|
|
125975
126206
|
throw new Error("LLM engine process exited before readiness checks completed");
|
|
125976
126207
|
}
|
|
125977
126208
|
const readiness = await this.checkEngineReadiness();
|
|
125978
126209
|
if (readiness === "ready") {
|
|
126210
|
+
// A stop() may have landed while the readiness request was
|
|
126211
|
+
// in flight; re-check before declaring ready.
|
|
126212
|
+
const lifecycleState = this.lifecycleState;
|
|
126213
|
+
if (lifecycleState === "stopping" || this.stopRequested) {
|
|
126214
|
+
throw new Error("LLM engine startup interrupted by stop request");
|
|
126215
|
+
}
|
|
125979
126216
|
return;
|
|
125980
126217
|
}
|
|
125981
126218
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
125982
126219
|
}
|
|
125983
126220
|
const stderrTail = this.engineProcess?.stderr?.slice(-1e3);
|
|
126221
|
+
if (this.engine === "custom") {
|
|
126222
|
+
const baseURL = this.customBaseURL ?? "(missing)";
|
|
126223
|
+
const discovered = this.discoveredModelNames;
|
|
126224
|
+
throw new Error(discovered.length > 0
|
|
126225
|
+
? `Custom engine endpoint at ${baseURL} exposed models (${discovered.join(", ")}) but never reported ready within ${Math.round(maxWaitMs / 1000)}s`
|
|
126226
|
+
: `Custom engine endpoint at ${baseURL} did not expose a model via /v1/models within ${Math.round(maxWaitMs / 1000)}s. Verify the server is running and reachable from conduit (if conduit runs in a container, use the host address instead of localhost)`);
|
|
126227
|
+
}
|
|
125984
126228
|
throw new Error(stderrTail
|
|
125985
126229
|
? `LLM engine failed readiness checks within timeout. Last engine output:\n${stderrTail}`
|
|
125986
126230
|
: "LLM engine failed readiness checks within timeout");
|
|
@@ -125993,19 +126237,32 @@ class ModelManager extends EventEmitter {
|
|
|
125993
126237
|
}
|
|
125994
126238
|
startHealthPoll() {
|
|
125995
126239
|
this.clearHealthPoll();
|
|
125996
|
-
this.logger.info("Starting background health poll
|
|
126240
|
+
this.logger.info("Starting background engine health poll", {
|
|
126241
|
+
agentEngineType: this.engine
|
|
126242
|
+
});
|
|
125997
126243
|
this.healthPollInterval = setInterval(() => {
|
|
125998
|
-
if (!this.engineProcess) {
|
|
126244
|
+
if (!this.engineProcess && this.engine !== "custom") {
|
|
125999
126245
|
this.clearHealthPoll();
|
|
126000
126246
|
return;
|
|
126001
126247
|
}
|
|
126002
126248
|
this.checkEngineReadiness()
|
|
126003
126249
|
.then(readiness => {
|
|
126004
126250
|
if (readiness === "ready") {
|
|
126005
|
-
this.
|
|
126006
|
-
|
|
126007
|
-
|
|
126008
|
-
|
|
126251
|
+
if (this.lifecycleState === "errored" ||
|
|
126252
|
+
this.lifecycleState === "starting") {
|
|
126253
|
+
this.lifecycleState = "running";
|
|
126254
|
+
this.reachedRunningState = true;
|
|
126255
|
+
this.emit("engineReady");
|
|
126256
|
+
}
|
|
126257
|
+
if (this.engine !== "custom") {
|
|
126258
|
+
this.clearHealthPoll();
|
|
126259
|
+
}
|
|
126260
|
+
return;
|
|
126261
|
+
}
|
|
126262
|
+
if (this.engine === "custom" &&
|
|
126263
|
+
readiness === "unreachable" &&
|
|
126264
|
+
this.lifecycleState === "running") {
|
|
126265
|
+
this.recordEngineError(new Error(`Custom engine endpoint unreachable: ${this.customBaseURL}`));
|
|
126009
126266
|
}
|
|
126010
126267
|
})
|
|
126011
126268
|
.catch(() => {
|
|
@@ -126030,6 +126287,15 @@ class ModelManager extends EventEmitter {
|
|
|
126030
126287
|
this.lastEngineError = err;
|
|
126031
126288
|
this.emit("engineError", err);
|
|
126032
126289
|
}
|
|
126290
|
+
requireCustomBaseURL() {
|
|
126291
|
+
const baseURL = this.customBaseURL;
|
|
126292
|
+
if (!baseURL) {
|
|
126293
|
+
throw new ConfigurationInvalidError({
|
|
126294
|
+
message: "Custom engine requires a base URL"
|
|
126295
|
+
});
|
|
126296
|
+
}
|
|
126297
|
+
return baseURL;
|
|
126298
|
+
}
|
|
126033
126299
|
async releaseDownloadLock() {
|
|
126034
126300
|
const handle = this.downloadLockHandle;
|
|
126035
126301
|
if (!handle)
|
|
@@ -126096,6 +126362,8 @@ class ModelManager extends EventEmitter {
|
|
|
126096
126362
|
async startEngineProcess() {
|
|
126097
126363
|
const targetDir = join(this.modelsDirectory, this.uniqueName);
|
|
126098
126364
|
switch (this.engine) {
|
|
126365
|
+
case "custom":
|
|
126366
|
+
return null;
|
|
126099
126367
|
case "exllamav3":
|
|
126100
126368
|
return startExllamav3.call(this, {
|
|
126101
126369
|
enginePort: this.enginePort,
|
|
@@ -126407,8 +126675,9 @@ function isEngineUsageChunk(value) {
|
|
|
126407
126675
|
}
|
|
126408
126676
|
return true;
|
|
126409
126677
|
}
|
|
126410
|
-
function monitorEngineResponseStream({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt }) {
|
|
126678
|
+
function monitorEngineResponseStream({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt, responseModelName }) {
|
|
126411
126679
|
const startedAt = requestStartedAt ?? Date.now();
|
|
126680
|
+
const clientModelName = responseModelName ?? null;
|
|
126412
126681
|
const passThrough = new PassThrough();
|
|
126413
126682
|
passThrough.on("error", (error) => {
|
|
126414
126683
|
logger.error("Engine response stream error", {
|
|
@@ -126420,53 +126689,61 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126420
126689
|
let firstChunkAt = null;
|
|
126421
126690
|
let usage = null;
|
|
126422
126691
|
let buffer = "";
|
|
126692
|
+
let pendingFragment = "";
|
|
126423
126693
|
let completed = false;
|
|
126424
|
-
function
|
|
126425
|
-
const
|
|
126426
|
-
|
|
126427
|
-
|
|
126428
|
-
|
|
126429
|
-
|
|
126430
|
-
|
|
126431
|
-
|
|
126432
|
-
|
|
126694
|
+
function rewriteDataLine(rawLine) {
|
|
126695
|
+
const line = rawLine.trim();
|
|
126696
|
+
if (!line.startsWith("data:")) {
|
|
126697
|
+
return rawLine;
|
|
126698
|
+
}
|
|
126699
|
+
const payload = line.slice(5).trim();
|
|
126700
|
+
if (!payload || payload === "[DONE]") {
|
|
126701
|
+
return rawLine;
|
|
126702
|
+
}
|
|
126703
|
+
try {
|
|
126704
|
+
const parsed = JSON.parse(payload);
|
|
126705
|
+
let modified = false;
|
|
126706
|
+
if (coerceToolCallArguments(parsed)) {
|
|
126707
|
+
modified = true;
|
|
126433
126708
|
}
|
|
126434
|
-
|
|
126435
|
-
|
|
126436
|
-
|
|
126437
|
-
|
|
126709
|
+
if (clientModelName !== null &&
|
|
126710
|
+
typeof parsed.model === "string" &&
|
|
126711
|
+
parsed.model !== clientModelName) {
|
|
126712
|
+
parsed.model = clientModelName;
|
|
126713
|
+
modified = true;
|
|
126438
126714
|
}
|
|
126439
|
-
|
|
126440
|
-
const
|
|
126441
|
-
|
|
126442
|
-
|
|
126715
|
+
if (parsed.usage) {
|
|
126716
|
+
const usageChunk = parsed.usage;
|
|
126717
|
+
const effectiveContext = getEffectiveContextLength({
|
|
126718
|
+
contextLength,
|
|
126719
|
+
engineConfig,
|
|
126720
|
+
engineType
|
|
126721
|
+
});
|
|
126722
|
+
if (usageChunk.context_usage === undefined &&
|
|
126723
|
+
usageChunk.prompt_tokens !== undefined &&
|
|
126724
|
+
effectiveContext !== null) {
|
|
126725
|
+
usageChunk.context_usage = usageChunk.prompt_tokens / effectiveContext;
|
|
126443
126726
|
modified = true;
|
|
126444
126727
|
}
|
|
126445
|
-
if (parsed.usage) {
|
|
126446
|
-
const usageChunk = parsed.usage;
|
|
126447
|
-
const effectiveContext = getEffectiveContextLength({
|
|
126448
|
-
contextLength,
|
|
126449
|
-
engineConfig,
|
|
126450
|
-
engineType
|
|
126451
|
-
});
|
|
126452
|
-
if (usageChunk.context_usage === undefined &&
|
|
126453
|
-
usageChunk.prompt_tokens !== undefined &&
|
|
126454
|
-
effectiveContext !== null) {
|
|
126455
|
-
usageChunk.context_usage = usageChunk.prompt_tokens / effectiveContext;
|
|
126456
|
-
modified = true;
|
|
126457
|
-
}
|
|
126458
|
-
}
|
|
126459
|
-
if (modified) {
|
|
126460
|
-
modifiedLines.push("data: " + JSON.stringify(parsed));
|
|
126461
|
-
continue;
|
|
126462
|
-
}
|
|
126463
126728
|
}
|
|
126464
|
-
|
|
126465
|
-
|
|
126729
|
+
if (modified) {
|
|
126730
|
+
return "data: " + JSON.stringify(parsed);
|
|
126466
126731
|
}
|
|
126467
|
-
modifiedLines.push(rawLine);
|
|
126468
126732
|
}
|
|
126469
|
-
|
|
126733
|
+
catch (_error) {
|
|
126734
|
+
// Ignore malformed chunks
|
|
126735
|
+
}
|
|
126736
|
+
return rawLine;
|
|
126737
|
+
}
|
|
126738
|
+
// SSE events can split across transport chunks: hold back the trailing
|
|
126739
|
+
// (newline-less) fragment and only rewrite complete data lines, so a
|
|
126740
|
+
// partial JSON event is never forwarded with its upstream model name.
|
|
126741
|
+
function modifyChunkWithUsage(chunk, flush = false) {
|
|
126742
|
+
const combined = pendingFragment + chunk.toString("utf8");
|
|
126743
|
+
const lines = combined.split("\n");
|
|
126744
|
+
pendingFragment = flush ? "" : (lines.pop() ?? "");
|
|
126745
|
+
const modifiedLines = lines.map(rewriteDataLine);
|
|
126746
|
+
return Buffer.from(modifiedLines.length > 0 ? modifiedLines.join("\n") + (flush ? "" : "\n") : "", "utf8");
|
|
126470
126747
|
}
|
|
126471
126748
|
function parseUsageFromBuffer() {
|
|
126472
126749
|
const lines = buffer.split("\n");
|
|
@@ -126562,6 +126839,9 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126562
126839
|
if (buffer.length > 0) {
|
|
126563
126840
|
parseUsageFromBuffer();
|
|
126564
126841
|
}
|
|
126842
|
+
if (pendingFragment.length > 0) {
|
|
126843
|
+
passThrough.write(modifyChunkWithUsage(Buffer.from("", "utf8"), true));
|
|
126844
|
+
}
|
|
126565
126845
|
logEngineMetrics({
|
|
126566
126846
|
agentEngineType,
|
|
126567
126847
|
level: "info",
|
|
@@ -126604,9 +126884,11 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126604
126884
|
stream: passThrough
|
|
126605
126885
|
};
|
|
126606
126886
|
}
|
|
126607
|
-
function monitorEngineResponseSingle({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt }) {
|
|
126887
|
+
function monitorEngineResponseSingle({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt, responseModelName }) {
|
|
126608
126888
|
const maxUsageCaptureBytes = 1024 * 1024;
|
|
126609
126889
|
const startedAt = requestStartedAt ?? Date.now();
|
|
126890
|
+
const clientModelName = responseModelName ?? null;
|
|
126891
|
+
const rewriteBuffer = clientModelName !== null ? [] : null;
|
|
126610
126892
|
const passThrough = new PassThrough();
|
|
126611
126893
|
passThrough.on("error", (error) => {
|
|
126612
126894
|
logger.error("Engine response stream error", {
|
|
@@ -126662,7 +126944,12 @@ function monitorEngineResponseSingle({ agentEngineType, body, contextLength, eng
|
|
|
126662
126944
|
usageChunks.length = 0;
|
|
126663
126945
|
}
|
|
126664
126946
|
}
|
|
126665
|
-
|
|
126947
|
+
if (rewriteBuffer) {
|
|
126948
|
+
rewriteBuffer.push(chunkBuffer);
|
|
126949
|
+
}
|
|
126950
|
+
else {
|
|
126951
|
+
passThrough.write(chunkBuffer);
|
|
126952
|
+
}
|
|
126666
126953
|
});
|
|
126667
126954
|
body.once("error", err => {
|
|
126668
126955
|
logEngineMetrics({
|
|
@@ -126722,6 +127009,24 @@ function monitorEngineResponseSingle({ agentEngineType, body, contextLength, eng
|
|
|
126722
127009
|
responseBytes,
|
|
126723
127010
|
usage
|
|
126724
127011
|
});
|
|
127012
|
+
if (rewriteBuffer) {
|
|
127013
|
+
const original = Buffer.concat(rewriteBuffer);
|
|
127014
|
+
let output = original;
|
|
127015
|
+
try {
|
|
127016
|
+
const parsed = JSON.parse(original.toString("utf8"));
|
|
127017
|
+
if (parsed !== null &&
|
|
127018
|
+
typeof parsed === "object" &&
|
|
127019
|
+
typeof parsed.model === "string" &&
|
|
127020
|
+
parsed.model !== clientModelName) {
|
|
127021
|
+
parsed.model = clientModelName;
|
|
127022
|
+
output = Buffer.from(JSON.stringify(parsed), "utf8");
|
|
127023
|
+
}
|
|
127024
|
+
}
|
|
127025
|
+
catch (_error) {
|
|
127026
|
+
// Non-JSON body: pass through untouched
|
|
127027
|
+
}
|
|
127028
|
+
passThrough.write(output);
|
|
127029
|
+
}
|
|
126725
127030
|
finalize(null);
|
|
126726
127031
|
passThrough.end();
|
|
126727
127032
|
});
|
|
@@ -126858,7 +127163,7 @@ function applyChatTemplateKwargs({ body, model }) {
|
|
|
126858
127163
|
}
|
|
126859
127164
|
return payload;
|
|
126860
127165
|
}
|
|
126861
|
-
function serializeRequestBody$1(body, { model, path } = {}) {
|
|
127166
|
+
function serializeRequestBody$1(body, { model, path, servedModelName } = {}) {
|
|
126862
127167
|
if (!isPlainObject$a(body)) {
|
|
126863
127168
|
const payload = typeof body === "string" ? body : JSON.stringify(body);
|
|
126864
127169
|
return {
|
|
@@ -126867,6 +127172,9 @@ function serializeRequestBody$1(body, { model, path } = {}) {
|
|
|
126867
127172
|
};
|
|
126868
127173
|
}
|
|
126869
127174
|
let requestPayload = { ...body };
|
|
127175
|
+
if (servedModelName) {
|
|
127176
|
+
requestPayload.model = servedModelName;
|
|
127177
|
+
}
|
|
126870
127178
|
if (path === "/v1/chat/completions" && model) {
|
|
126871
127179
|
requestPayload = applyChatTemplateKwargs({ body: requestPayload, model });
|
|
126872
127180
|
}
|
|
@@ -126923,8 +127231,15 @@ async function proxyEmbeddingsRoute({ body, conduitConfiguration, endpointId, lo
|
|
|
126923
127231
|
}
|
|
126924
127232
|
const engineType = conduitConfiguration.engineConfig?.type ?? null;
|
|
126925
127233
|
const engineConfig = conduitConfiguration.engineConfig ?? null;
|
|
127234
|
+
const servedModelName = modelManager.resolvedServedModelName;
|
|
127235
|
+
const clientModelName = isPlainObject$a(body) && typeof body.model === "string" ? body.model : null;
|
|
126926
127236
|
const serializedBody = isPlainObject$a(body)
|
|
126927
|
-
? JSON.stringify(
|
|
127237
|
+
? JSON.stringify(servedModelName
|
|
127238
|
+
? {
|
|
127239
|
+
...body,
|
|
127240
|
+
model: servedModelName
|
|
127241
|
+
}
|
|
127242
|
+
: body)
|
|
126928
127243
|
: typeof body === "string"
|
|
126929
127244
|
? body
|
|
126930
127245
|
: JSON.stringify(body);
|
|
@@ -127042,14 +127357,27 @@ async function proxyEmbeddingsRoute({ body, conduitConfiguration, endpointId, lo
|
|
|
127042
127357
|
onComplete: onMonitoringComplete,
|
|
127043
127358
|
requestBodyBytes,
|
|
127044
127359
|
requestPath: "/v1/embeddings",
|
|
127045
|
-
requestStartedAt
|
|
127360
|
+
requestStartedAt,
|
|
127361
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127046
127362
|
});
|
|
127047
127363
|
return {
|
|
127048
127364
|
body: monitoredResponse.stream,
|
|
127049
|
-
headers:
|
|
127365
|
+
headers: buildProxyResponseHeaders(response.headers, Boolean(servedModelName)),
|
|
127050
127366
|
status: response.status
|
|
127051
127367
|
};
|
|
127052
127368
|
}
|
|
127369
|
+
/**
|
|
127370
|
+
* Forwards upstream headers, dropping content-length when the response body
|
|
127371
|
+
* is rewritten (model-name substitution changes its length; a stale
|
|
127372
|
+
* content-length desyncs the stream and aborts downstream readers).
|
|
127373
|
+
*/
|
|
127374
|
+
function buildProxyResponseHeaders(headers, rewriteActive) {
|
|
127375
|
+
const forwarded = Object.fromEntries(headers.entries());
|
|
127376
|
+
if (rewriteActive) {
|
|
127377
|
+
delete forwarded["content-length"];
|
|
127378
|
+
}
|
|
127379
|
+
return forwarded;
|
|
127380
|
+
}
|
|
127053
127381
|
async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointId, logger, modelID, modelManager, path, reportMetrics, signal }) {
|
|
127054
127382
|
function normalizeTokenCount(value) {
|
|
127055
127383
|
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
@@ -127067,8 +127395,10 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127067
127395
|
}
|
|
127068
127396
|
const engineType = conduitConfiguration.engineConfig?.type ?? null;
|
|
127069
127397
|
const engineConfig = conduitConfiguration.engineConfig ?? null;
|
|
127398
|
+
const servedModelName = modelManager.resolvedServedModelName;
|
|
127399
|
+
const clientModelName = isPlainObject$a(body) && typeof body.model === "string" ? body.model : null;
|
|
127070
127400
|
const effectiveBody = modelManager.model.multimodalEnabled ? body : stripImagesFromBody(body);
|
|
127071
|
-
const { bytes: requestBodyBytes, payload: serializedBody } = serializeRequestBody$1(effectiveBody, { model: modelManager.model, path });
|
|
127401
|
+
const { bytes: requestBodyBytes, payload: serializedBody } = serializeRequestBody$1(effectiveBody, { model: modelManager.model, path, servedModelName });
|
|
127072
127402
|
const requestStartedAt = Date.now();
|
|
127073
127403
|
const requestBody = JSON.parse(serializedBody);
|
|
127074
127404
|
const streamRequested = requestBody.stream === true;
|
|
@@ -127208,7 +127538,8 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127208
127538
|
onComplete: onMonitoringComplete,
|
|
127209
127539
|
requestBodyBytes,
|
|
127210
127540
|
requestPath: path,
|
|
127211
|
-
requestStartedAt
|
|
127541
|
+
requestStartedAt,
|
|
127542
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127212
127543
|
})
|
|
127213
127544
|
: monitorEngineResponseSingle({
|
|
127214
127545
|
agentEngineType: engineType ?? "unknown",
|
|
@@ -127220,11 +127551,12 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127220
127551
|
onComplete: onMonitoringComplete,
|
|
127221
127552
|
requestBodyBytes,
|
|
127222
127553
|
requestPath: path,
|
|
127223
|
-
requestStartedAt
|
|
127554
|
+
requestStartedAt,
|
|
127555
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127224
127556
|
});
|
|
127225
127557
|
return {
|
|
127226
127558
|
body: monitoredResponse.stream,
|
|
127227
|
-
headers:
|
|
127559
|
+
headers: buildProxyResponseHeaders(response.headers, Boolean(servedModelName)),
|
|
127228
127560
|
status: response.status
|
|
127229
127561
|
};
|
|
127230
127562
|
}
|
|
@@ -138514,13 +138846,16 @@ async function detectDockerVersion() {
|
|
|
138514
138846
|
* Flattens flat CLI extra-arg tokens into [arg, value] pairs, sorted by ARG NAME (ascending, ties by
|
|
138515
138847
|
* value). `--flag=value` pairs split on the first `=`; a bare `--flag` consumes the following token
|
|
138516
138848
|
* as its value when that token does not start with "-" (classic CLI convention); anything else
|
|
138517
|
-
* (flags, non-strings) is dropped.
|
|
138849
|
+
* (flags, non-strings) is dropped. Secret-like option values are masked before pairing so they
|
|
138850
|
+
* never reach execution reports.
|
|
138518
138851
|
*/
|
|
138519
138852
|
function pairExtraArgs(tokens) {
|
|
138520
138853
|
if (!Array.isArray(tokens)) {
|
|
138521
138854
|
return [];
|
|
138522
138855
|
}
|
|
138523
|
-
|
|
138856
|
+
// Non-string tokens become empty strings: they keep their position as a
|
|
138857
|
+
// non-consumable barrier while allowing secret masking over string tokens.
|
|
138858
|
+
const list = redactSecretArgs(tokens.map(token => (typeof token === "string" ? token : "")));
|
|
138524
138859
|
const pairs = [];
|
|
138525
138860
|
let index = 0;
|
|
138526
138861
|
while (index < list.length) {
|
|
@@ -138656,9 +138991,9 @@ async function createApplication({ abortController, apiClient, configuration, lo
|
|
|
138656
138991
|
}
|
|
138657
138992
|
const reporter = new EngineExecutionReporter({
|
|
138658
138993
|
buildContext: () => {
|
|
138659
|
-
const engineType =
|
|
138660
|
-
"llama.cpp");
|
|
138994
|
+
const engineType = conduitConfiguration.engineConfig?.type ?? "llama.cpp";
|
|
138661
138995
|
const versions = {
|
|
138996
|
+
custom: null,
|
|
138662
138997
|
exllamav3: machine?.exllamav3Version ?? null,
|
|
138663
138998
|
"llama.cpp": machine?.llamaCppVersion ?? null,
|
|
138664
138999
|
"mlx-lm": machine?.mlxlmVersion ?? null,
|
|
@@ -138858,23 +139193,33 @@ async function createApplication({ abortController, apiClient, configuration, lo
|
|
|
138858
139193
|
logger.info("Engine start requested");
|
|
138859
139194
|
reporter.beginStartup(new Date());
|
|
138860
139195
|
try {
|
|
138861
|
-
|
|
138862
|
-
|
|
138863
|
-
|
|
138864
|
-
|
|
138865
|
-
|
|
138866
|
-
|
|
138867
|
-
|
|
138868
|
-
|
|
138869
|
-
|
|
138870
|
-
|
|
139196
|
+
if (modelManager.engine === "custom") {
|
|
139197
|
+
conduitStateManager.setState({
|
|
139198
|
+
state: "bootingEngine"
|
|
139199
|
+
});
|
|
139200
|
+
await conduitStateReportManager.reportNow();
|
|
139201
|
+
}
|
|
139202
|
+
else {
|
|
139203
|
+
conduitStateManager.setState({
|
|
139204
|
+
modelFileName,
|
|
139205
|
+
modelName,
|
|
139206
|
+
state: "downloadingModelFiles",
|
|
139207
|
+
totalProgress: {
|
|
139208
|
+
file: 0,
|
|
139209
|
+
total: 0
|
|
139210
|
+
}
|
|
139211
|
+
});
|
|
139212
|
+
await conduitStateReportManager.reportNow();
|
|
139213
|
+
}
|
|
138871
139214
|
await modelManager.prepare({
|
|
138872
139215
|
onDownloadProgress: reportDownloadProgress
|
|
138873
139216
|
});
|
|
138874
|
-
|
|
138875
|
-
|
|
138876
|
-
|
|
138877
|
-
|
|
139217
|
+
if (modelManager.engine !== "custom") {
|
|
139218
|
+
conduitStateManager.setState({
|
|
139219
|
+
state: "bootingEngine"
|
|
139220
|
+
});
|
|
139221
|
+
await conduitStateReportManager.reportNow();
|
|
139222
|
+
}
|
|
138878
139223
|
await modelManager.start();
|
|
138879
139224
|
}
|
|
138880
139225
|
catch (error) {
|
|
@@ -139145,7 +139490,11 @@ function createModelManagerFromConfig(conduitConfiguration, configuration, logge
|
|
|
139145
139490
|
return new ModelManager({
|
|
139146
139491
|
contextLength: conduitConfiguration.contextLength ?? null,
|
|
139147
139492
|
engineConfig: engineConfig
|
|
139148
|
-
? {
|
|
139493
|
+
? {
|
|
139494
|
+
baseUrl: engineConfig.baseUrl ?? null,
|
|
139495
|
+
extraArgs: engineConfig.extraArgs,
|
|
139496
|
+
type: engineConfig.type
|
|
139497
|
+
}
|
|
139149
139498
|
: null,
|
|
139150
139499
|
enginePort: configuration.enginePort,
|
|
139151
139500
|
engineType: engineConfig?.type ?? "llama.cpp",
|
|
@@ -139156,7 +139505,11 @@ function createModelManagerFromConfig(conduitConfiguration, configuration, logge
|
|
|
139156
139505
|
}
|
|
139157
139506
|
function getConduitModelFileName(configuration) {
|
|
139158
139507
|
const { source } = configuration.targetModel;
|
|
139159
|
-
|
|
139508
|
+
if (source.type === "huggingface")
|
|
139509
|
+
return source.slug;
|
|
139510
|
+
if (source.type === "storage")
|
|
139511
|
+
return source.irid;
|
|
139512
|
+
return configuration.targetModel.id;
|
|
139160
139513
|
}
|
|
139161
139514
|
function getConduitModelName(configuration) {
|
|
139162
139515
|
return configuration.targetModel.id;
|
|
@@ -343824,16 +344177,15 @@ function buildSourceCreateBody(options) {
|
|
|
343824
344177
|
if (!options.engine) {
|
|
343825
344178
|
throw new Error("--engine is required (engine ID from `engine create`)");
|
|
343826
344179
|
}
|
|
343827
|
-
if (!options.model) {
|
|
343828
|
-
throw new Error("--model is required (model ID from `models create`)");
|
|
343829
|
-
}
|
|
343830
344180
|
validateULID({ flagName: "--engine", value: options.engine });
|
|
343831
|
-
validateULID({ flagName: "--model", value: options.model });
|
|
343832
344181
|
const body = {
|
|
343833
344182
|
engineId: options.engine,
|
|
343834
|
-
modelID: options.model,
|
|
344183
|
+
modelID: options.model ?? null,
|
|
343835
344184
|
name: options.name
|
|
343836
344185
|
};
|
|
344186
|
+
if (options.model !== undefined) {
|
|
344187
|
+
validateULID({ flagName: "--model", value: options.model });
|
|
344188
|
+
}
|
|
343837
344189
|
if (options.quant !== undefined) {
|
|
343838
344190
|
validateQuant(options.quant);
|
|
343839
344191
|
body.quantizationLabel = options.quant;
|
|
@@ -343875,7 +344227,7 @@ function registerSourceCommands({ program }) {
|
|
|
343875
344227
|
.option("--engine <id>", "Engine ID to run this source with (required)")
|
|
343876
344228
|
.option("--id <ulid>", "Target an existing source by ID (requires --update)")
|
|
343877
344229
|
.option("--key <value>", "API key (required, no environment variable fallback)")
|
|
343878
|
-
.option("--model <id>", "Model ID to serve (required)")
|
|
344230
|
+
.option("--model <id>", "Model ID to serve (required unless the engine type is custom)")
|
|
343879
344231
|
.option("--name <name>", "Source name (matched by --update)")
|
|
343880
344232
|
.option("--quant <label>", "Quantization variant label (eg Q4_K_M)")
|
|
343881
344233
|
.option("--update", "Update an existing source matched by name (or --id) instead of erroring")
|