@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.sea.cjs
CHANGED
|
@@ -19900,6 +19900,7 @@ object$5({
|
|
|
19900
19900
|
});
|
|
19901
19901
|
|
|
19902
19902
|
const LLMEngineSchema = _enum$1([
|
|
19903
|
+
"custom",
|
|
19903
19904
|
"exllamav3",
|
|
19904
19905
|
"llama.cpp",
|
|
19905
19906
|
"mlx-lm",
|
|
@@ -19908,8 +19909,25 @@ const LLMEngineSchema = _enum$1([
|
|
|
19908
19909
|
"vllm"
|
|
19909
19910
|
]);
|
|
19910
19911
|
const EngineConfigSchema = object$5({
|
|
19912
|
+
baseUrl: string$2().url().nullable().default(null),
|
|
19911
19913
|
extraArgs: array$1(string$2()),
|
|
19912
19914
|
type: LLMEngineSchema
|
|
19915
|
+
})
|
|
19916
|
+
.superRefine((config, ctx) => {
|
|
19917
|
+
if (config.type === "custom" && config.baseUrl === null) {
|
|
19918
|
+
ctx.addIssue({
|
|
19919
|
+
code: "custom",
|
|
19920
|
+
message: "Engine type 'custom' requires a base URL",
|
|
19921
|
+
path: ["baseUrl"]
|
|
19922
|
+
});
|
|
19923
|
+
}
|
|
19924
|
+
if (config.type !== "custom" && config.baseUrl !== null) {
|
|
19925
|
+
ctx.addIssue({
|
|
19926
|
+
code: "custom",
|
|
19927
|
+
message: "Base URL is only valid for engine type 'custom'",
|
|
19928
|
+
path: ["baseUrl"]
|
|
19929
|
+
});
|
|
19930
|
+
}
|
|
19913
19931
|
});
|
|
19914
19932
|
const LLMModelFormatSchema = _enum$1([
|
|
19915
19933
|
// VLLM / SGLang / TensorRT-LLM
|
|
@@ -19968,14 +19986,18 @@ const LLMModelSchema = object$5({
|
|
|
19968
19986
|
id: string$2().min(1),
|
|
19969
19987
|
multimodalEnabled: boolean$1(),
|
|
19970
19988
|
source: discriminatedUnion("type", [
|
|
19989
|
+
// Custom engines: no local model record, serving is external
|
|
19971
19990
|
object$5({
|
|
19972
|
-
|
|
19973
|
-
type: literal("storage")
|
|
19991
|
+
type: literal("external")
|
|
19974
19992
|
}),
|
|
19975
19993
|
object$5({
|
|
19976
19994
|
modelSecret: string$2().min(1).nullable(),
|
|
19977
19995
|
slug: string$2().min(1),
|
|
19978
19996
|
type: literal("huggingface")
|
|
19997
|
+
}),
|
|
19998
|
+
object$5({
|
|
19999
|
+
irid: IRIDSchema,
|
|
20000
|
+
type: literal("storage")
|
|
19979
20001
|
})
|
|
19980
20002
|
]),
|
|
19981
20003
|
taskType: LLMModelTaskTypeSchema,
|
|
@@ -21119,7 +21141,8 @@ const CreateModelResponseSchema = object$5({
|
|
|
21119
21141
|
const CreateSourceBodySchema = object$5({
|
|
21120
21142
|
contextLength: number$1().int().positive().max(1048576).optional(),
|
|
21121
21143
|
engineId: ULIDSchema,
|
|
21122
|
-
|
|
21144
|
+
// Optional for custom engines, which serve externally managed models
|
|
21145
|
+
modelID: ULIDSchema.nullable().optional(),
|
|
21123
21146
|
name: ResourceNameSchema,
|
|
21124
21147
|
quantizationLabel: string$2().min(1).max(128).optional()
|
|
21125
21148
|
});
|
|
@@ -21168,7 +21191,7 @@ const SourceDetailResponseSchema = object$5({
|
|
|
21168
21191
|
const UpdateSourceBodySchema = object$5({
|
|
21169
21192
|
contextLength: number$1().int().positive().nullable().optional(),
|
|
21170
21193
|
engineId: ULIDSchema.nullable().optional(),
|
|
21171
|
-
modelID: ULIDSchema.optional(),
|
|
21194
|
+
modelID: ULIDSchema.nullable().optional(),
|
|
21172
21195
|
name: ResourceNameSchema.optional(),
|
|
21173
21196
|
quantizationLabel: string$2().min(1).max(128).nullable().optional()
|
|
21174
21197
|
});
|
|
@@ -21292,6 +21315,7 @@ const CreateEndpointResponseSchema = object$5({
|
|
|
21292
21315
|
id: ULIDSchema
|
|
21293
21316
|
});
|
|
21294
21317
|
const EngineOutputSchema = object$5({
|
|
21318
|
+
baseUrl: string$2().nullable(),
|
|
21295
21319
|
created: string$2(),
|
|
21296
21320
|
extraArgs: array$1(string$2()),
|
|
21297
21321
|
id: ULIDSchema,
|
|
@@ -21300,11 +21324,29 @@ const EngineOutputSchema = object$5({
|
|
|
21300
21324
|
updated: string$2()
|
|
21301
21325
|
});
|
|
21302
21326
|
const CreateEngineBodySchema = object$5({
|
|
21327
|
+
baseUrl: string$2().url().nullable().optional(),
|
|
21303
21328
|
extraArgs: array$1(string$2()).optional(),
|
|
21304
21329
|
name: ResourceNameSchema,
|
|
21305
21330
|
type: LLMEngineSchema
|
|
21331
|
+
})
|
|
21332
|
+
.superRefine((body, ctx) => {
|
|
21333
|
+
if (body.type === "custom" && !body.baseUrl) {
|
|
21334
|
+
ctx.addIssue({
|
|
21335
|
+
code: "custom",
|
|
21336
|
+
message: "Engine type 'custom' requires a base URL",
|
|
21337
|
+
path: ["baseUrl"]
|
|
21338
|
+
});
|
|
21339
|
+
}
|
|
21340
|
+
if (body.type !== "custom" && body.baseUrl) {
|
|
21341
|
+
ctx.addIssue({
|
|
21342
|
+
code: "custom",
|
|
21343
|
+
message: "Base URL is only valid for engine type 'custom'",
|
|
21344
|
+
path: ["baseUrl"]
|
|
21345
|
+
});
|
|
21346
|
+
}
|
|
21306
21347
|
});
|
|
21307
21348
|
const UpdateEngineBodySchema = object$5({
|
|
21349
|
+
baseUrl: string$2().url().nullable().optional(),
|
|
21308
21350
|
extraArgs: array$1(string$2()).optional(),
|
|
21309
21351
|
name: ResourceNameSchema.optional(),
|
|
21310
21352
|
type: LLMEngineSchema.optional()
|
|
@@ -22110,6 +22152,11 @@ const RecommendedModelSchema = object$5({
|
|
|
22110
22152
|
const recommendedModels = RecommendedModelSchema.array().parse(modelsData);
|
|
22111
22153
|
|
|
22112
22154
|
const ENGINE_API_COMPATIBILITY = {
|
|
22155
|
+
custom: {
|
|
22156
|
+
nativeAnthropicMessages: false,
|
|
22157
|
+
supportsEmbeddings: true,
|
|
22158
|
+
supportsVision: true
|
|
22159
|
+
},
|
|
22113
22160
|
exllamav3: {
|
|
22114
22161
|
nativeAnthropicMessages: false,
|
|
22115
22162
|
supportsEmbeddings: false,
|
|
@@ -111759,6 +111806,27 @@ function registerEndpointCommands({ program }) {
|
|
|
111759
111806
|
}
|
|
111760
111807
|
|
|
111761
111808
|
const ENGINE_TYPES = LLMEngineSchema.options;
|
|
111809
|
+
function isValidURL(value) {
|
|
111810
|
+
try {
|
|
111811
|
+
const url = new URL(value);
|
|
111812
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
111813
|
+
}
|
|
111814
|
+
catch (_error) {
|
|
111815
|
+
return false;
|
|
111816
|
+
}
|
|
111817
|
+
}
|
|
111818
|
+
function validateEngineBaseURL({ baseUrl, type }) {
|
|
111819
|
+
if (type === "custom" && !baseUrl) {
|
|
111820
|
+
throw new Error("Engine type 'custom' requires --base-url");
|
|
111821
|
+
}
|
|
111822
|
+
if (baseUrl !== undefined && type !== "custom") {
|
|
111823
|
+
throw new Error("--base-url is only valid for engine type 'custom'");
|
|
111824
|
+
}
|
|
111825
|
+
if (baseUrl !== undefined && baseUrl !== "" && !isValidURL(baseUrl)) {
|
|
111826
|
+
throw new Error(`Invalid --base-url value: ${baseUrl}`);
|
|
111827
|
+
}
|
|
111828
|
+
return baseUrl === "" ? null : (baseUrl ?? null);
|
|
111829
|
+
}
|
|
111762
111830
|
function buildEngineCreateBody(options) {
|
|
111763
111831
|
if (!options.name) {
|
|
111764
111832
|
throw new Error("--name is required");
|
|
@@ -111769,7 +111837,12 @@ function buildEngineCreateBody(options) {
|
|
|
111769
111837
|
if (!ENGINE_TYPES.includes(options.type)) {
|
|
111770
111838
|
throw new Error(`Invalid engine type: ${options.type} (expected one of: ${ENGINE_TYPES.join(", ")})`);
|
|
111771
111839
|
}
|
|
111840
|
+
const baseUrl = validateEngineBaseURL({
|
|
111841
|
+
baseUrl: options.baseUrl,
|
|
111842
|
+
type: options.type
|
|
111843
|
+
});
|
|
111772
111844
|
return {
|
|
111845
|
+
baseUrl,
|
|
111773
111846
|
extraArgs: options.arg ?? [],
|
|
111774
111847
|
name: options.name,
|
|
111775
111848
|
type: options.type
|
|
@@ -111787,6 +111860,24 @@ function buildEngineUpdateBody(options) {
|
|
|
111787
111860
|
}
|
|
111788
111861
|
if (options.arg !== undefined)
|
|
111789
111862
|
body.extraArgs = options.arg;
|
|
111863
|
+
if (options.baseUrl !== undefined) {
|
|
111864
|
+
const targetType = body.type;
|
|
111865
|
+
if (targetType !== undefined) {
|
|
111866
|
+
body.baseUrl = validateEngineBaseURL({
|
|
111867
|
+
baseUrl: options.baseUrl,
|
|
111868
|
+
type: targetType
|
|
111869
|
+
});
|
|
111870
|
+
}
|
|
111871
|
+
else if (options.baseUrl === "") {
|
|
111872
|
+
body.baseUrl = null;
|
|
111873
|
+
}
|
|
111874
|
+
else if (!isValidURL(options.baseUrl)) {
|
|
111875
|
+
throw new Error(`Invalid --base-url value: ${options.baseUrl}`);
|
|
111876
|
+
}
|
|
111877
|
+
else {
|
|
111878
|
+
body.baseUrl = options.baseUrl;
|
|
111879
|
+
}
|
|
111880
|
+
}
|
|
111790
111881
|
return body;
|
|
111791
111882
|
}
|
|
111792
111883
|
|
|
@@ -111800,10 +111891,11 @@ function registerEngineCommands({ program }) {
|
|
|
111800
111891
|
.description("Create or update an inference engine resource")
|
|
111801
111892
|
.option("--api-url <url>", "API base URL (required, no environment variable fallback)")
|
|
111802
111893
|
.option("--arg <flag>", 'Raw engine CLI flag, repeatable (eg --arg "--flash-attn on")', collect, [])
|
|
111894
|
+
.option("--base-url <url>", "Base URL of an external OpenAI-compatible server (engine type 'custom' only)")
|
|
111803
111895
|
.option("--id <ulid>", "Target an existing engine by ID (requires --update)")
|
|
111804
111896
|
.option("--key <value>", "API key (required, no environment variable fallback)")
|
|
111805
111897
|
.option("--name <name>", "Engine name (matched by --update)")
|
|
111806
|
-
.option("--type <type>", "Engine type: llama.cpp|vllm|sglang|tensorrt-llm|mlx-lm|exllamav3")
|
|
111898
|
+
.option("--type <type>", "Engine type: llama.cpp|vllm|sglang|tensorrt-llm|mlx-lm|exllamav3|custom")
|
|
111807
111899
|
.option("--update", "Update an existing engine matched by name (or --id) instead of erroring")
|
|
111808
111900
|
.action(async (options) => {
|
|
111809
111901
|
const { apiURL, apiKey } = resolveManagementConnection(options);
|
|
@@ -117803,6 +117895,9 @@ async function getChatTemplateEngineArgs({ engine, model, targetDirectory }) {
|
|
|
117803
117895
|
return [];
|
|
117804
117896
|
const flag = FLAG_BASED_ENGINE_ARGS[engine];
|
|
117805
117897
|
if (!flag) {
|
|
117898
|
+
if (engine === "custom") {
|
|
117899
|
+
console.warn("[chatTemplate] Custom engines manage their own serving; ignoring chat template override");
|
|
117900
|
+
}
|
|
117806
117901
|
if (engine === "tensorrt-llm") {
|
|
117807
117902
|
console.warn("[chatTemplate] TensorRT-LLM does not support chat template overrides; ignoring");
|
|
117808
117903
|
}
|
|
@@ -124700,7 +124795,7 @@ function matchesQuantizationVariant({ filePath, variant }) {
|
|
|
124700
124795
|
return segments.slice(0, -1).some(segment => matcher.test(segment));
|
|
124701
124796
|
}
|
|
124702
124797
|
async function findQuantizedModelTarget({ model, path }) {
|
|
124703
|
-
if (model.source.type
|
|
124798
|
+
if (model.source.type !== "huggingface") {
|
|
124704
124799
|
throw new Error("Model storage not supported yet");
|
|
124705
124800
|
}
|
|
124706
124801
|
if (model.format !== "gguf") {
|
|
@@ -125615,7 +125710,11 @@ function sanitizeSegment(value) {
|
|
|
125615
125710
|
.replace(new RegExp(`${SEPARATOR}{2,}`, "g"), SEPARATOR);
|
|
125616
125711
|
}
|
|
125617
125712
|
function createModelStorageKey(model) {
|
|
125618
|
-
const identifier = model.source.type === "huggingface"
|
|
125713
|
+
const identifier = model.source.type === "huggingface"
|
|
125714
|
+
? model.source.slug
|
|
125715
|
+
: model.source.type === "storage"
|
|
125716
|
+
? model.source.irid
|
|
125717
|
+
: model.id;
|
|
125619
125718
|
return `${model.source.type}${SEPARATOR}${sanitizeSegment(identifier)}`;
|
|
125620
125719
|
}
|
|
125621
125720
|
|
|
@@ -125691,8 +125790,11 @@ class ModelManager extends EventEmitter {
|
|
|
125691
125790
|
uniqueName;
|
|
125692
125791
|
contextLength;
|
|
125693
125792
|
logger;
|
|
125793
|
+
discoveredModelNames = [];
|
|
125794
|
+
customProbeCount = 0;
|
|
125694
125795
|
engineProcess = null;
|
|
125695
125796
|
healthPollInterval = null;
|
|
125797
|
+
lastCustomReadinessReport = null;
|
|
125696
125798
|
lastEngineError = null;
|
|
125697
125799
|
lifecycleState = "stopped";
|
|
125698
125800
|
downloadLockHandle = null;
|
|
@@ -125723,6 +125825,7 @@ class ModelManager extends EventEmitter {
|
|
|
125723
125825
|
}
|
|
125724
125826
|
async fetchOpenAI(path, opts) {
|
|
125725
125827
|
switch (this.engine) {
|
|
125828
|
+
case "custom":
|
|
125726
125829
|
case "exllamav3":
|
|
125727
125830
|
case "llama.cpp":
|
|
125728
125831
|
case "mlx-lm":
|
|
@@ -125730,6 +125833,9 @@ class ModelManager extends EventEmitter {
|
|
|
125730
125833
|
case "tensorrt-llm":
|
|
125731
125834
|
case "vllm": {
|
|
125732
125835
|
this.logger.debug(`Fetching from engine: ${path}`);
|
|
125836
|
+
const baseURL = this.engine === "custom"
|
|
125837
|
+
? this.requireCustomBaseURL()
|
|
125838
|
+
: `http://localhost:${this.enginePort}`;
|
|
125733
125839
|
const callerSignal = opts?.signal;
|
|
125734
125840
|
const controller = new AbortController();
|
|
125735
125841
|
const timeout = setTimeout(() => {
|
|
@@ -125740,7 +125846,7 @@ class ModelManager extends EventEmitter {
|
|
|
125740
125846
|
: controller.signal;
|
|
125741
125847
|
try {
|
|
125742
125848
|
const fetchStartedAt = Date.now();
|
|
125743
|
-
const response = await undiciExports.fetch(joinURL(
|
|
125849
|
+
const response = await undiciExports.fetch(joinURL(baseURL, path), {
|
|
125744
125850
|
...opts,
|
|
125745
125851
|
dispatcher: ENGINE_AGENT,
|
|
125746
125852
|
headers: {
|
|
@@ -125775,6 +125881,11 @@ class ModelManager extends EventEmitter {
|
|
|
125775
125881
|
modelID: this.model.id
|
|
125776
125882
|
});
|
|
125777
125883
|
switch (this.engine) {
|
|
125884
|
+
case "custom":
|
|
125885
|
+
if (this.model.chatTemplate) {
|
|
125886
|
+
this.logger.warn("Chat template overrides are ignored for custom engines: the remote server manages its own serving");
|
|
125887
|
+
}
|
|
125888
|
+
break;
|
|
125778
125889
|
case "exllamav3":
|
|
125779
125890
|
case "llama.cpp":
|
|
125780
125891
|
case "mlx-lm":
|
|
@@ -125830,7 +125941,9 @@ class ModelManager extends EventEmitter {
|
|
|
125830
125941
|
});
|
|
125831
125942
|
try {
|
|
125832
125943
|
this.engineProcess = await this.startEngineProcess();
|
|
125833
|
-
|
|
125944
|
+
if (this.engineProcess) {
|
|
125945
|
+
this.bindEngineProcessEvents(this.engineProcess);
|
|
125946
|
+
}
|
|
125834
125947
|
this.logger.info("Started LLM engine", {
|
|
125835
125948
|
agentEngineType: this.engine
|
|
125836
125949
|
});
|
|
@@ -125849,7 +125962,7 @@ class ModelManager extends EventEmitter {
|
|
|
125849
125962
|
if (!alreadyEmitted) {
|
|
125850
125963
|
this.emit("engineError", err);
|
|
125851
125964
|
}
|
|
125852
|
-
if (this.engineProcess) {
|
|
125965
|
+
if (this.engineProcess || this.engine === "custom") {
|
|
125853
125966
|
this.startHealthPoll();
|
|
125854
125967
|
}
|
|
125855
125968
|
throw err;
|
|
@@ -125857,6 +125970,9 @@ class ModelManager extends EventEmitter {
|
|
|
125857
125970
|
this.lifecycleState = "running";
|
|
125858
125971
|
this.reachedRunningState = true;
|
|
125859
125972
|
this.emit("engineReady");
|
|
125973
|
+
if (this.engine === "custom") {
|
|
125974
|
+
this.startHealthPoll();
|
|
125975
|
+
}
|
|
125860
125976
|
}
|
|
125861
125977
|
async stop() {
|
|
125862
125978
|
if (this.lifecycleState === "stopping") {
|
|
@@ -125877,6 +125993,8 @@ class ModelManager extends EventEmitter {
|
|
|
125877
125993
|
this.clearHealthPoll();
|
|
125878
125994
|
const processManager = this.engineProcess;
|
|
125879
125995
|
if (!processManager) {
|
|
125996
|
+
this.stopRequested = true;
|
|
125997
|
+
this.reachedRunningState = false;
|
|
125880
125998
|
this.lifecycleState = "stopped";
|
|
125881
125999
|
return;
|
|
125882
126000
|
}
|
|
@@ -125902,11 +126020,27 @@ class ModelManager extends EventEmitter {
|
|
|
125902
126020
|
get state() {
|
|
125903
126021
|
return this.lifecycleState;
|
|
125904
126022
|
}
|
|
126023
|
+
get resolvedServedModelName() {
|
|
126024
|
+
if (this.engine !== "custom")
|
|
126025
|
+
return null;
|
|
126026
|
+
return this.discoveredModelNames[0] ?? null;
|
|
126027
|
+
}
|
|
125905
126028
|
get wasRunning() {
|
|
125906
126029
|
return this.reachedRunningState;
|
|
125907
126030
|
}
|
|
126031
|
+
get customBaseURL() {
|
|
126032
|
+
if (this.engine !== "custom")
|
|
126033
|
+
return null;
|
|
126034
|
+
const baseUrl = this.engineConfig?.baseUrl;
|
|
126035
|
+
if (typeof baseUrl !== "string" || baseUrl.length === 0)
|
|
126036
|
+
return null;
|
|
126037
|
+
return baseUrl.replace(/\/+$/, "");
|
|
126038
|
+
}
|
|
125908
126039
|
async checkEngineReadiness() {
|
|
125909
126040
|
switch (this.engine) {
|
|
126041
|
+
case "custom": {
|
|
126042
|
+
return this.checkCustomReadiness();
|
|
126043
|
+
}
|
|
125910
126044
|
case "llama.cpp": {
|
|
125911
126045
|
return this.checkLlamacppReadiness();
|
|
125912
126046
|
}
|
|
@@ -125923,6 +126057,98 @@ class ModelManager extends EventEmitter {
|
|
|
125923
126057
|
return "ready";
|
|
125924
126058
|
}
|
|
125925
126059
|
}
|
|
126060
|
+
async checkCustomReadiness() {
|
|
126061
|
+
const baseURL = this.customBaseURL;
|
|
126062
|
+
if (!baseURL) {
|
|
126063
|
+
this.reportCustomReadinessChange("unreachable", "", "no base URL configured");
|
|
126064
|
+
return "unreachable";
|
|
126065
|
+
}
|
|
126066
|
+
const probeURL = joinURL(baseURL, "/v1/models");
|
|
126067
|
+
this.customProbeCount++;
|
|
126068
|
+
try {
|
|
126069
|
+
const response = await undiciExports.fetch(probeURL, {
|
|
126070
|
+
method: "GET",
|
|
126071
|
+
signal: AbortSignal.timeout(5000)
|
|
126072
|
+
});
|
|
126073
|
+
if (response.status === 503) {
|
|
126074
|
+
this.logger.debug("Custom engine probe: server loading", {
|
|
126075
|
+
attempt: this.customProbeCount,
|
|
126076
|
+
probeURL
|
|
126077
|
+
});
|
|
126078
|
+
this.reportCustomReadinessChange("loading", baseURL, "server loading (HTTP 503)");
|
|
126079
|
+
return "loading";
|
|
126080
|
+
}
|
|
126081
|
+
if (!response.ok) {
|
|
126082
|
+
const reason = `HTTP ${response.status} from /v1/models`;
|
|
126083
|
+
this.logger.debug("Custom engine probe: not ready", {
|
|
126084
|
+
attempt: this.customProbeCount,
|
|
126085
|
+
probeURL,
|
|
126086
|
+
reason
|
|
126087
|
+
});
|
|
126088
|
+
this.reportCustomReadinessChange("unreachable", baseURL, reason);
|
|
126089
|
+
return "unreachable";
|
|
126090
|
+
}
|
|
126091
|
+
const payload = (await response.json());
|
|
126092
|
+
const models = Array.isArray(payload.data) ? payload.data : [];
|
|
126093
|
+
const modelIDs = models
|
|
126094
|
+
.map(model => {
|
|
126095
|
+
if (model === null || typeof model !== "object")
|
|
126096
|
+
return null;
|
|
126097
|
+
const id = model.id;
|
|
126098
|
+
return typeof id === "string" ? id : null;
|
|
126099
|
+
})
|
|
126100
|
+
.filter((id) => id !== null);
|
|
126101
|
+
if (modelIDs.length === 0) {
|
|
126102
|
+
this.logger.debug("Custom engine probe: no models exposed yet", {
|
|
126103
|
+
attempt: this.customProbeCount,
|
|
126104
|
+
probeURL
|
|
126105
|
+
});
|
|
126106
|
+
this.reportCustomReadinessChange("loading", baseURL, "/v1/models returned no models");
|
|
126107
|
+
return "loading";
|
|
126108
|
+
}
|
|
126109
|
+
if (modelIDs.join("\n") !== this.discoveredModelNames.join("\n")) {
|
|
126110
|
+
this.discoveredModelNames = modelIDs;
|
|
126111
|
+
this.logger.info("Discovered models on custom engine endpoint", {
|
|
126112
|
+
engineBaseURL: baseURL,
|
|
126113
|
+
models: modelIDs,
|
|
126114
|
+
selectedModel: modelIDs[0]
|
|
126115
|
+
});
|
|
126116
|
+
}
|
|
126117
|
+
return "ready";
|
|
126118
|
+
}
|
|
126119
|
+
catch (error) {
|
|
126120
|
+
const reason = asError(error).message;
|
|
126121
|
+
this.logger.debug("Custom engine probe: request failed", {
|
|
126122
|
+
attempt: this.customProbeCount,
|
|
126123
|
+
probeURL,
|
|
126124
|
+
reason
|
|
126125
|
+
});
|
|
126126
|
+
this.reportCustomReadinessChange("unreachable", baseURL, reason);
|
|
126127
|
+
return "unreachable";
|
|
126128
|
+
}
|
|
126129
|
+
}
|
|
126130
|
+
reportCustomReadinessChange(readiness, baseURL, reason) {
|
|
126131
|
+
const key = `${readiness}:${reason}`;
|
|
126132
|
+
if (key === this.lastCustomReadinessReport) {
|
|
126133
|
+
// Same outcome as the last report: surface a heartbeat every 15
|
|
126134
|
+
// attempts (~30s while booting) so progress stays visible.
|
|
126135
|
+
if (this.customProbeCount % 15 === 0) {
|
|
126136
|
+
this.logger.info("Still waiting for custom engine endpoint", {
|
|
126137
|
+
attempt: this.customProbeCount,
|
|
126138
|
+
engineBaseURL: baseURL,
|
|
126139
|
+
reason
|
|
126140
|
+
});
|
|
126141
|
+
}
|
|
126142
|
+
return;
|
|
126143
|
+
}
|
|
126144
|
+
this.lastCustomReadinessReport = key;
|
|
126145
|
+
this.logger.warn(readiness === "loading"
|
|
126146
|
+
? "Custom engine endpoint is loading — waiting for /v1/models to expose a model"
|
|
126147
|
+
: "Custom engine endpoint unreachable — verify the server is running and reachable from conduit", {
|
|
126148
|
+
engineBaseURL: baseURL,
|
|
126149
|
+
reason
|
|
126150
|
+
});
|
|
126151
|
+
}
|
|
125926
126152
|
async checkGenericHealthReadiness() {
|
|
125927
126153
|
try {
|
|
125928
126154
|
const response = await undiciExports.fetch(joinURL(`http://localhost:${this.enginePort}`, "/health"), {
|
|
@@ -125978,23 +126204,41 @@ class ModelManager extends EventEmitter {
|
|
|
125978
126204
|
}
|
|
125979
126205
|
}
|
|
125980
126206
|
async waitForEngineReady() {
|
|
125981
|
-
const maxWaitMs = 15 * 60 * 1000;
|
|
126207
|
+
const maxWaitMs = Number.parseInt(process.env.ENGINE_STARTUP_TIMEOUT_MS ?? "", 10) || 15 * 60 * 1000;
|
|
125982
126208
|
const pollIntervalMs = 2000;
|
|
125983
126209
|
const start = Date.now();
|
|
126210
|
+
if (this.engine === "custom") {
|
|
126211
|
+
this.logger.info(`Connecting to external OpenAI-compatible server at ${this.customBaseURL ?? "(no base URL configured)"} — serving begins once /v1/models exposes a model`, {
|
|
126212
|
+
engineBaseURL: this.customBaseURL ?? ""
|
|
126213
|
+
});
|
|
126214
|
+
}
|
|
125984
126215
|
while (Date.now() - start < maxWaitMs) {
|
|
125985
|
-
if (this.lifecycleState === "stopping") {
|
|
126216
|
+
if (this.lifecycleState === "stopping" || this.stopRequested) {
|
|
125986
126217
|
throw new Error("LLM engine startup interrupted by stop request");
|
|
125987
126218
|
}
|
|
125988
|
-
if (!this.engineProcess) {
|
|
126219
|
+
if (!this.engineProcess && this.engine !== "custom") {
|
|
125989
126220
|
throw new Error("LLM engine process exited before readiness checks completed");
|
|
125990
126221
|
}
|
|
125991
126222
|
const readiness = await this.checkEngineReadiness();
|
|
125992
126223
|
if (readiness === "ready") {
|
|
126224
|
+
// A stop() may have landed while the readiness request was
|
|
126225
|
+
// in flight; re-check before declaring ready.
|
|
126226
|
+
const lifecycleState = this.lifecycleState;
|
|
126227
|
+
if (lifecycleState === "stopping" || this.stopRequested) {
|
|
126228
|
+
throw new Error("LLM engine startup interrupted by stop request");
|
|
126229
|
+
}
|
|
125993
126230
|
return;
|
|
125994
126231
|
}
|
|
125995
126232
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
125996
126233
|
}
|
|
125997
126234
|
const stderrTail = this.engineProcess?.stderr?.slice(-1e3);
|
|
126235
|
+
if (this.engine === "custom") {
|
|
126236
|
+
const baseURL = this.customBaseURL ?? "(missing)";
|
|
126237
|
+
const discovered = this.discoveredModelNames;
|
|
126238
|
+
throw new Error(discovered.length > 0
|
|
126239
|
+
? `Custom engine endpoint at ${baseURL} exposed models (${discovered.join(", ")}) but never reported ready within ${Math.round(maxWaitMs / 1000)}s`
|
|
126240
|
+
: `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)`);
|
|
126241
|
+
}
|
|
125998
126242
|
throw new Error(stderrTail
|
|
125999
126243
|
? `LLM engine failed readiness checks within timeout. Last engine output:\n${stderrTail}`
|
|
126000
126244
|
: "LLM engine failed readiness checks within timeout");
|
|
@@ -126007,19 +126251,32 @@ class ModelManager extends EventEmitter {
|
|
|
126007
126251
|
}
|
|
126008
126252
|
startHealthPoll() {
|
|
126009
126253
|
this.clearHealthPoll();
|
|
126010
|
-
this.logger.info("Starting background health poll
|
|
126254
|
+
this.logger.info("Starting background engine health poll", {
|
|
126255
|
+
agentEngineType: this.engine
|
|
126256
|
+
});
|
|
126011
126257
|
this.healthPollInterval = setInterval(() => {
|
|
126012
|
-
if (!this.engineProcess) {
|
|
126258
|
+
if (!this.engineProcess && this.engine !== "custom") {
|
|
126013
126259
|
this.clearHealthPoll();
|
|
126014
126260
|
return;
|
|
126015
126261
|
}
|
|
126016
126262
|
this.checkEngineReadiness()
|
|
126017
126263
|
.then(readiness => {
|
|
126018
126264
|
if (readiness === "ready") {
|
|
126019
|
-
this.
|
|
126020
|
-
|
|
126021
|
-
|
|
126022
|
-
|
|
126265
|
+
if (this.lifecycleState === "errored" ||
|
|
126266
|
+
this.lifecycleState === "starting") {
|
|
126267
|
+
this.lifecycleState = "running";
|
|
126268
|
+
this.reachedRunningState = true;
|
|
126269
|
+
this.emit("engineReady");
|
|
126270
|
+
}
|
|
126271
|
+
if (this.engine !== "custom") {
|
|
126272
|
+
this.clearHealthPoll();
|
|
126273
|
+
}
|
|
126274
|
+
return;
|
|
126275
|
+
}
|
|
126276
|
+
if (this.engine === "custom" &&
|
|
126277
|
+
readiness === "unreachable" &&
|
|
126278
|
+
this.lifecycleState === "running") {
|
|
126279
|
+
this.recordEngineError(new Error(`Custom engine endpoint unreachable: ${this.customBaseURL}`));
|
|
126023
126280
|
}
|
|
126024
126281
|
})
|
|
126025
126282
|
.catch(() => {
|
|
@@ -126044,6 +126301,15 @@ class ModelManager extends EventEmitter {
|
|
|
126044
126301
|
this.lastEngineError = err;
|
|
126045
126302
|
this.emit("engineError", err);
|
|
126046
126303
|
}
|
|
126304
|
+
requireCustomBaseURL() {
|
|
126305
|
+
const baseURL = this.customBaseURL;
|
|
126306
|
+
if (!baseURL) {
|
|
126307
|
+
throw new ConfigurationInvalidError({
|
|
126308
|
+
message: "Custom engine requires a base URL"
|
|
126309
|
+
});
|
|
126310
|
+
}
|
|
126311
|
+
return baseURL;
|
|
126312
|
+
}
|
|
126047
126313
|
async releaseDownloadLock() {
|
|
126048
126314
|
const handle = this.downloadLockHandle;
|
|
126049
126315
|
if (!handle)
|
|
@@ -126110,6 +126376,8 @@ class ModelManager extends EventEmitter {
|
|
|
126110
126376
|
async startEngineProcess() {
|
|
126111
126377
|
const targetDir = path$1.join(this.modelsDirectory, this.uniqueName);
|
|
126112
126378
|
switch (this.engine) {
|
|
126379
|
+
case "custom":
|
|
126380
|
+
return null;
|
|
126113
126381
|
case "exllamav3":
|
|
126114
126382
|
return startExllamav3.call(this, {
|
|
126115
126383
|
enginePort: this.enginePort,
|
|
@@ -126421,8 +126689,9 @@ function isEngineUsageChunk(value) {
|
|
|
126421
126689
|
}
|
|
126422
126690
|
return true;
|
|
126423
126691
|
}
|
|
126424
|
-
function monitorEngineResponseStream({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt }) {
|
|
126692
|
+
function monitorEngineResponseStream({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt, responseModelName }) {
|
|
126425
126693
|
const startedAt = requestStartedAt ?? Date.now();
|
|
126694
|
+
const clientModelName = responseModelName ?? null;
|
|
126426
126695
|
const passThrough = new require$$0$8.PassThrough();
|
|
126427
126696
|
passThrough.on("error", (error) => {
|
|
126428
126697
|
logger.error("Engine response stream error", {
|
|
@@ -126434,53 +126703,61 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126434
126703
|
let firstChunkAt = null;
|
|
126435
126704
|
let usage = null;
|
|
126436
126705
|
let buffer = "";
|
|
126706
|
+
let pendingFragment = "";
|
|
126437
126707
|
let completed = false;
|
|
126438
|
-
function
|
|
126439
|
-
const
|
|
126440
|
-
|
|
126441
|
-
|
|
126442
|
-
|
|
126443
|
-
|
|
126444
|
-
|
|
126445
|
-
|
|
126446
|
-
|
|
126708
|
+
function rewriteDataLine(rawLine) {
|
|
126709
|
+
const line = rawLine.trim();
|
|
126710
|
+
if (!line.startsWith("data:")) {
|
|
126711
|
+
return rawLine;
|
|
126712
|
+
}
|
|
126713
|
+
const payload = line.slice(5).trim();
|
|
126714
|
+
if (!payload || payload === "[DONE]") {
|
|
126715
|
+
return rawLine;
|
|
126716
|
+
}
|
|
126717
|
+
try {
|
|
126718
|
+
const parsed = JSON.parse(payload);
|
|
126719
|
+
let modified = false;
|
|
126720
|
+
if (coerceToolCallArguments(parsed)) {
|
|
126721
|
+
modified = true;
|
|
126447
126722
|
}
|
|
126448
|
-
|
|
126449
|
-
|
|
126450
|
-
|
|
126451
|
-
|
|
126723
|
+
if (clientModelName !== null &&
|
|
126724
|
+
typeof parsed.model === "string" &&
|
|
126725
|
+
parsed.model !== clientModelName) {
|
|
126726
|
+
parsed.model = clientModelName;
|
|
126727
|
+
modified = true;
|
|
126452
126728
|
}
|
|
126453
|
-
|
|
126454
|
-
const
|
|
126455
|
-
|
|
126456
|
-
|
|
126729
|
+
if (parsed.usage) {
|
|
126730
|
+
const usageChunk = parsed.usage;
|
|
126731
|
+
const effectiveContext = getEffectiveContextLength({
|
|
126732
|
+
contextLength,
|
|
126733
|
+
engineConfig,
|
|
126734
|
+
engineType
|
|
126735
|
+
});
|
|
126736
|
+
if (usageChunk.context_usage === undefined &&
|
|
126737
|
+
usageChunk.prompt_tokens !== undefined &&
|
|
126738
|
+
effectiveContext !== null) {
|
|
126739
|
+
usageChunk.context_usage = usageChunk.prompt_tokens / effectiveContext;
|
|
126457
126740
|
modified = true;
|
|
126458
126741
|
}
|
|
126459
|
-
if (parsed.usage) {
|
|
126460
|
-
const usageChunk = parsed.usage;
|
|
126461
|
-
const effectiveContext = getEffectiveContextLength({
|
|
126462
|
-
contextLength,
|
|
126463
|
-
engineConfig,
|
|
126464
|
-
engineType
|
|
126465
|
-
});
|
|
126466
|
-
if (usageChunk.context_usage === undefined &&
|
|
126467
|
-
usageChunk.prompt_tokens !== undefined &&
|
|
126468
|
-
effectiveContext !== null) {
|
|
126469
|
-
usageChunk.context_usage = usageChunk.prompt_tokens / effectiveContext;
|
|
126470
|
-
modified = true;
|
|
126471
|
-
}
|
|
126472
|
-
}
|
|
126473
|
-
if (modified) {
|
|
126474
|
-
modifiedLines.push("data: " + JSON.stringify(parsed));
|
|
126475
|
-
continue;
|
|
126476
|
-
}
|
|
126477
126742
|
}
|
|
126478
|
-
|
|
126479
|
-
|
|
126743
|
+
if (modified) {
|
|
126744
|
+
return "data: " + JSON.stringify(parsed);
|
|
126480
126745
|
}
|
|
126481
|
-
modifiedLines.push(rawLine);
|
|
126482
126746
|
}
|
|
126483
|
-
|
|
126747
|
+
catch (_error) {
|
|
126748
|
+
// Ignore malformed chunks
|
|
126749
|
+
}
|
|
126750
|
+
return rawLine;
|
|
126751
|
+
}
|
|
126752
|
+
// SSE events can split across transport chunks: hold back the trailing
|
|
126753
|
+
// (newline-less) fragment and only rewrite complete data lines, so a
|
|
126754
|
+
// partial JSON event is never forwarded with its upstream model name.
|
|
126755
|
+
function modifyChunkWithUsage(chunk, flush = false) {
|
|
126756
|
+
const combined = pendingFragment + chunk.toString("utf8");
|
|
126757
|
+
const lines = combined.split("\n");
|
|
126758
|
+
pendingFragment = flush ? "" : (lines.pop() ?? "");
|
|
126759
|
+
const modifiedLines = lines.map(rewriteDataLine);
|
|
126760
|
+
return Buffer.from(modifiedLines.length > 0 ? modifiedLines.join("\n") + (flush ? "" : "\n") : "", "utf8");
|
|
126484
126761
|
}
|
|
126485
126762
|
function parseUsageFromBuffer() {
|
|
126486
126763
|
const lines = buffer.split("\n");
|
|
@@ -126576,6 +126853,9 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126576
126853
|
if (buffer.length > 0) {
|
|
126577
126854
|
parseUsageFromBuffer();
|
|
126578
126855
|
}
|
|
126856
|
+
if (pendingFragment.length > 0) {
|
|
126857
|
+
passThrough.write(modifyChunkWithUsage(Buffer.from("", "utf8"), true));
|
|
126858
|
+
}
|
|
126579
126859
|
logEngineMetrics({
|
|
126580
126860
|
agentEngineType,
|
|
126581
126861
|
level: "info",
|
|
@@ -126618,9 +126898,11 @@ function monitorEngineResponseStream({ agentEngineType, body, contextLength, eng
|
|
|
126618
126898
|
stream: passThrough
|
|
126619
126899
|
};
|
|
126620
126900
|
}
|
|
126621
|
-
function monitorEngineResponseSingle({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt }) {
|
|
126901
|
+
function monitorEngineResponseSingle({ agentEngineType, body, contextLength, engineConfig, engineType, logger, onComplete, requestBodyBytes, requestPath, requestStartedAt, responseModelName }) {
|
|
126622
126902
|
const maxUsageCaptureBytes = 1024 * 1024;
|
|
126623
126903
|
const startedAt = requestStartedAt ?? Date.now();
|
|
126904
|
+
const clientModelName = responseModelName ?? null;
|
|
126905
|
+
const rewriteBuffer = clientModelName !== null ? [] : null;
|
|
126624
126906
|
const passThrough = new require$$0$8.PassThrough();
|
|
126625
126907
|
passThrough.on("error", (error) => {
|
|
126626
126908
|
logger.error("Engine response stream error", {
|
|
@@ -126676,7 +126958,12 @@ function monitorEngineResponseSingle({ agentEngineType, body, contextLength, eng
|
|
|
126676
126958
|
usageChunks.length = 0;
|
|
126677
126959
|
}
|
|
126678
126960
|
}
|
|
126679
|
-
|
|
126961
|
+
if (rewriteBuffer) {
|
|
126962
|
+
rewriteBuffer.push(chunkBuffer);
|
|
126963
|
+
}
|
|
126964
|
+
else {
|
|
126965
|
+
passThrough.write(chunkBuffer);
|
|
126966
|
+
}
|
|
126680
126967
|
});
|
|
126681
126968
|
body.once("error", err => {
|
|
126682
126969
|
logEngineMetrics({
|
|
@@ -126736,6 +127023,24 @@ function monitorEngineResponseSingle({ agentEngineType, body, contextLength, eng
|
|
|
126736
127023
|
responseBytes,
|
|
126737
127024
|
usage
|
|
126738
127025
|
});
|
|
127026
|
+
if (rewriteBuffer) {
|
|
127027
|
+
const original = Buffer.concat(rewriteBuffer);
|
|
127028
|
+
let output = original;
|
|
127029
|
+
try {
|
|
127030
|
+
const parsed = JSON.parse(original.toString("utf8"));
|
|
127031
|
+
if (parsed !== null &&
|
|
127032
|
+
typeof parsed === "object" &&
|
|
127033
|
+
typeof parsed.model === "string" &&
|
|
127034
|
+
parsed.model !== clientModelName) {
|
|
127035
|
+
parsed.model = clientModelName;
|
|
127036
|
+
output = Buffer.from(JSON.stringify(parsed), "utf8");
|
|
127037
|
+
}
|
|
127038
|
+
}
|
|
127039
|
+
catch (_error) {
|
|
127040
|
+
// Non-JSON body: pass through untouched
|
|
127041
|
+
}
|
|
127042
|
+
passThrough.write(output);
|
|
127043
|
+
}
|
|
126739
127044
|
finalize(null);
|
|
126740
127045
|
passThrough.end();
|
|
126741
127046
|
});
|
|
@@ -126872,7 +127177,7 @@ function applyChatTemplateKwargs({ body, model }) {
|
|
|
126872
127177
|
}
|
|
126873
127178
|
return payload;
|
|
126874
127179
|
}
|
|
126875
|
-
function serializeRequestBody$1(body, { model, path } = {}) {
|
|
127180
|
+
function serializeRequestBody$1(body, { model, path, servedModelName } = {}) {
|
|
126876
127181
|
if (!isPlainObject$a(body)) {
|
|
126877
127182
|
const payload = typeof body === "string" ? body : JSON.stringify(body);
|
|
126878
127183
|
return {
|
|
@@ -126881,6 +127186,9 @@ function serializeRequestBody$1(body, { model, path } = {}) {
|
|
|
126881
127186
|
};
|
|
126882
127187
|
}
|
|
126883
127188
|
let requestPayload = { ...body };
|
|
127189
|
+
if (servedModelName) {
|
|
127190
|
+
requestPayload.model = servedModelName;
|
|
127191
|
+
}
|
|
126884
127192
|
if (path === "/v1/chat/completions" && model) {
|
|
126885
127193
|
requestPayload = applyChatTemplateKwargs({ body: requestPayload, model });
|
|
126886
127194
|
}
|
|
@@ -126937,8 +127245,15 @@ async function proxyEmbeddingsRoute({ body, conduitConfiguration, endpointId, lo
|
|
|
126937
127245
|
}
|
|
126938
127246
|
const engineType = conduitConfiguration.engineConfig?.type ?? null;
|
|
126939
127247
|
const engineConfig = conduitConfiguration.engineConfig ?? null;
|
|
127248
|
+
const servedModelName = modelManager.resolvedServedModelName;
|
|
127249
|
+
const clientModelName = isPlainObject$a(body) && typeof body.model === "string" ? body.model : null;
|
|
126940
127250
|
const serializedBody = isPlainObject$a(body)
|
|
126941
|
-
? JSON.stringify(
|
|
127251
|
+
? JSON.stringify(servedModelName
|
|
127252
|
+
? {
|
|
127253
|
+
...body,
|
|
127254
|
+
model: servedModelName
|
|
127255
|
+
}
|
|
127256
|
+
: body)
|
|
126942
127257
|
: typeof body === "string"
|
|
126943
127258
|
? body
|
|
126944
127259
|
: JSON.stringify(body);
|
|
@@ -127056,14 +127371,27 @@ async function proxyEmbeddingsRoute({ body, conduitConfiguration, endpointId, lo
|
|
|
127056
127371
|
onComplete: onMonitoringComplete,
|
|
127057
127372
|
requestBodyBytes,
|
|
127058
127373
|
requestPath: "/v1/embeddings",
|
|
127059
|
-
requestStartedAt
|
|
127374
|
+
requestStartedAt,
|
|
127375
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127060
127376
|
});
|
|
127061
127377
|
return {
|
|
127062
127378
|
body: monitoredResponse.stream,
|
|
127063
|
-
headers:
|
|
127379
|
+
headers: buildProxyResponseHeaders(response.headers, Boolean(servedModelName)),
|
|
127064
127380
|
status: response.status
|
|
127065
127381
|
};
|
|
127066
127382
|
}
|
|
127383
|
+
/**
|
|
127384
|
+
* Forwards upstream headers, dropping content-length when the response body
|
|
127385
|
+
* is rewritten (model-name substitution changes its length; a stale
|
|
127386
|
+
* content-length desyncs the stream and aborts downstream readers).
|
|
127387
|
+
*/
|
|
127388
|
+
function buildProxyResponseHeaders(headers, rewriteActive) {
|
|
127389
|
+
const forwarded = Object.fromEntries(headers.entries());
|
|
127390
|
+
if (rewriteActive) {
|
|
127391
|
+
delete forwarded["content-length"];
|
|
127392
|
+
}
|
|
127393
|
+
return forwarded;
|
|
127394
|
+
}
|
|
127067
127395
|
async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointId, logger, modelID, modelManager, path, reportMetrics, signal }) {
|
|
127068
127396
|
function normalizeTokenCount(value) {
|
|
127069
127397
|
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
@@ -127081,8 +127409,10 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127081
127409
|
}
|
|
127082
127410
|
const engineType = conduitConfiguration.engineConfig?.type ?? null;
|
|
127083
127411
|
const engineConfig = conduitConfiguration.engineConfig ?? null;
|
|
127412
|
+
const servedModelName = modelManager.resolvedServedModelName;
|
|
127413
|
+
const clientModelName = isPlainObject$a(body) && typeof body.model === "string" ? body.model : null;
|
|
127084
127414
|
const effectiveBody = modelManager.model.multimodalEnabled ? body : stripImagesFromBody(body);
|
|
127085
|
-
const { bytes: requestBodyBytes, payload: serializedBody } = serializeRequestBody$1(effectiveBody, { model: modelManager.model, path });
|
|
127415
|
+
const { bytes: requestBodyBytes, payload: serializedBody } = serializeRequestBody$1(effectiveBody, { model: modelManager.model, path, servedModelName });
|
|
127086
127416
|
const requestStartedAt = Date.now();
|
|
127087
127417
|
const requestBody = JSON.parse(serializedBody);
|
|
127088
127418
|
const streamRequested = requestBody.stream === true;
|
|
@@ -127222,7 +127552,8 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127222
127552
|
onComplete: onMonitoringComplete,
|
|
127223
127553
|
requestBodyBytes,
|
|
127224
127554
|
requestPath: path,
|
|
127225
|
-
requestStartedAt
|
|
127555
|
+
requestStartedAt,
|
|
127556
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127226
127557
|
})
|
|
127227
127558
|
: monitorEngineResponseSingle({
|
|
127228
127559
|
agentEngineType: engineType ?? "unknown",
|
|
@@ -127234,11 +127565,12 @@ async function proxyOpenAIStreamingRoute({ body, conduitConfiguration, endpointI
|
|
|
127234
127565
|
onComplete: onMonitoringComplete,
|
|
127235
127566
|
requestBodyBytes,
|
|
127236
127567
|
requestPath: path,
|
|
127237
|
-
requestStartedAt
|
|
127568
|
+
requestStartedAt,
|
|
127569
|
+
responseModelName: servedModelName ? clientModelName : null
|
|
127238
127570
|
});
|
|
127239
127571
|
return {
|
|
127240
127572
|
body: monitoredResponse.stream,
|
|
127241
|
-
headers:
|
|
127573
|
+
headers: buildProxyResponseHeaders(response.headers, Boolean(servedModelName)),
|
|
127242
127574
|
status: response.status
|
|
127243
127575
|
};
|
|
127244
127576
|
}
|
|
@@ -158743,13 +159075,16 @@ async function detectDockerVersion() {
|
|
|
158743
159075
|
* Flattens flat CLI extra-arg tokens into [arg, value] pairs, sorted by ARG NAME (ascending, ties by
|
|
158744
159076
|
* value). `--flag=value` pairs split on the first `=`; a bare `--flag` consumes the following token
|
|
158745
159077
|
* as its value when that token does not start with "-" (classic CLI convention); anything else
|
|
158746
|
-
* (flags, non-strings) is dropped.
|
|
159078
|
+
* (flags, non-strings) is dropped. Secret-like option values are masked before pairing so they
|
|
159079
|
+
* never reach execution reports.
|
|
158747
159080
|
*/
|
|
158748
159081
|
function pairExtraArgs(tokens) {
|
|
158749
159082
|
if (!Array.isArray(tokens)) {
|
|
158750
159083
|
return [];
|
|
158751
159084
|
}
|
|
158752
|
-
|
|
159085
|
+
// Non-string tokens become empty strings: they keep their position as a
|
|
159086
|
+
// non-consumable barrier while allowing secret masking over string tokens.
|
|
159087
|
+
const list = redactSecretArgs(tokens.map(token => (typeof token === "string" ? token : "")));
|
|
158753
159088
|
const pairs = [];
|
|
158754
159089
|
let index = 0;
|
|
158755
159090
|
while (index < list.length) {
|
|
@@ -158885,9 +159220,9 @@ async function createApplication({ abortController, apiClient, configuration, lo
|
|
|
158885
159220
|
}
|
|
158886
159221
|
const reporter = new EngineExecutionReporter({
|
|
158887
159222
|
buildContext: () => {
|
|
158888
|
-
const engineType =
|
|
158889
|
-
"llama.cpp");
|
|
159223
|
+
const engineType = conduitConfiguration.engineConfig?.type ?? "llama.cpp";
|
|
158890
159224
|
const versions = {
|
|
159225
|
+
custom: null,
|
|
158891
159226
|
exllamav3: machine?.exllamav3Version ?? null,
|
|
158892
159227
|
"llama.cpp": machine?.llamaCppVersion ?? null,
|
|
158893
159228
|
"mlx-lm": machine?.mlxlmVersion ?? null,
|
|
@@ -159087,23 +159422,33 @@ async function createApplication({ abortController, apiClient, configuration, lo
|
|
|
159087
159422
|
logger.info("Engine start requested");
|
|
159088
159423
|
reporter.beginStartup(new Date());
|
|
159089
159424
|
try {
|
|
159090
|
-
|
|
159091
|
-
|
|
159092
|
-
|
|
159093
|
-
|
|
159094
|
-
|
|
159095
|
-
|
|
159096
|
-
|
|
159097
|
-
|
|
159098
|
-
|
|
159099
|
-
|
|
159425
|
+
if (modelManager.engine === "custom") {
|
|
159426
|
+
conduitStateManager.setState({
|
|
159427
|
+
state: "bootingEngine"
|
|
159428
|
+
});
|
|
159429
|
+
await conduitStateReportManager.reportNow();
|
|
159430
|
+
}
|
|
159431
|
+
else {
|
|
159432
|
+
conduitStateManager.setState({
|
|
159433
|
+
modelFileName,
|
|
159434
|
+
modelName,
|
|
159435
|
+
state: "downloadingModelFiles",
|
|
159436
|
+
totalProgress: {
|
|
159437
|
+
file: 0,
|
|
159438
|
+
total: 0
|
|
159439
|
+
}
|
|
159440
|
+
});
|
|
159441
|
+
await conduitStateReportManager.reportNow();
|
|
159442
|
+
}
|
|
159100
159443
|
await modelManager.prepare({
|
|
159101
159444
|
onDownloadProgress: reportDownloadProgress
|
|
159102
159445
|
});
|
|
159103
|
-
|
|
159104
|
-
|
|
159105
|
-
|
|
159106
|
-
|
|
159446
|
+
if (modelManager.engine !== "custom") {
|
|
159447
|
+
conduitStateManager.setState({
|
|
159448
|
+
state: "bootingEngine"
|
|
159449
|
+
});
|
|
159450
|
+
await conduitStateReportManager.reportNow();
|
|
159451
|
+
}
|
|
159107
159452
|
await modelManager.start();
|
|
159108
159453
|
}
|
|
159109
159454
|
catch (error) {
|
|
@@ -159374,7 +159719,11 @@ function createModelManagerFromConfig(conduitConfiguration, configuration, logge
|
|
|
159374
159719
|
return new ModelManager({
|
|
159375
159720
|
contextLength: conduitConfiguration.contextLength ?? null,
|
|
159376
159721
|
engineConfig: engineConfig
|
|
159377
|
-
? {
|
|
159722
|
+
? {
|
|
159723
|
+
baseUrl: engineConfig.baseUrl ?? null,
|
|
159724
|
+
extraArgs: engineConfig.extraArgs,
|
|
159725
|
+
type: engineConfig.type
|
|
159726
|
+
}
|
|
159378
159727
|
: null,
|
|
159379
159728
|
enginePort: configuration.enginePort,
|
|
159380
159729
|
engineType: engineConfig?.type ?? "llama.cpp",
|
|
@@ -159385,7 +159734,11 @@ function createModelManagerFromConfig(conduitConfiguration, configuration, logge
|
|
|
159385
159734
|
}
|
|
159386
159735
|
function getConduitModelFileName(configuration) {
|
|
159387
159736
|
const { source } = configuration.targetModel;
|
|
159388
|
-
|
|
159737
|
+
if (source.type === "huggingface")
|
|
159738
|
+
return source.slug;
|
|
159739
|
+
if (source.type === "storage")
|
|
159740
|
+
return source.irid;
|
|
159741
|
+
return configuration.targetModel.id;
|
|
159389
159742
|
}
|
|
159390
159743
|
function getConduitModelName(configuration) {
|
|
159391
159744
|
return configuration.targetModel.id;
|
|
@@ -364053,16 +364406,15 @@ function buildSourceCreateBody(options) {
|
|
|
364053
364406
|
if (!options.engine) {
|
|
364054
364407
|
throw new Error("--engine is required (engine ID from `engine create`)");
|
|
364055
364408
|
}
|
|
364056
|
-
if (!options.model) {
|
|
364057
|
-
throw new Error("--model is required (model ID from `models create`)");
|
|
364058
|
-
}
|
|
364059
364409
|
validateULID({ flagName: "--engine", value: options.engine });
|
|
364060
|
-
validateULID({ flagName: "--model", value: options.model });
|
|
364061
364410
|
const body = {
|
|
364062
364411
|
engineId: options.engine,
|
|
364063
|
-
modelID: options.model,
|
|
364412
|
+
modelID: options.model ?? null,
|
|
364064
364413
|
name: options.name
|
|
364065
364414
|
};
|
|
364415
|
+
if (options.model !== undefined) {
|
|
364416
|
+
validateULID({ flagName: "--model", value: options.model });
|
|
364417
|
+
}
|
|
364066
364418
|
if (options.quant !== undefined) {
|
|
364067
364419
|
validateQuant(options.quant);
|
|
364068
364420
|
body.quantizationLabel = options.quant;
|
|
@@ -364104,7 +364456,7 @@ function registerSourceCommands({ program }) {
|
|
|
364104
364456
|
.option("--engine <id>", "Engine ID to run this source with (required)")
|
|
364105
364457
|
.option("--id <ulid>", "Target an existing source by ID (requires --update)")
|
|
364106
364458
|
.option("--key <value>", "API key (required, no environment variable fallback)")
|
|
364107
|
-
.option("--model <id>", "Model ID to serve (required)")
|
|
364459
|
+
.option("--model <id>", "Model ID to serve (required unless the engine type is custom)")
|
|
364108
364460
|
.option("--name <name>", "Source name (matched by --update)")
|
|
364109
364461
|
.option("--quant <label>", "Quantization variant label (eg Q4_K_M)")
|
|
364110
364462
|
.option("--update", "Update an existing source matched by name (or --id) instead of erroring")
|