mslxdff 0.1.62 → 0.1.64
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/bin/mslxdff.js +396 -39
- package/package.json +1 -1
- package/src/auto.js +13 -0
- package/src/chat/config.js +2 -1
- package/src/chat/prompt.js +1 -1
- package/src/chat/repl.js +12 -49
- package/src/chat/stats.js +1 -1
- package/src/chat/upstream.js +183 -5
- package/src/providers/generic.js +17 -4
- package/src/providers/workbuddy.js +16 -5
- package/src/routes/index.js +10 -1
- package/src/routes/models-route.js +28 -0
- package/src/state.js +55 -10
package/src/state.js
CHANGED
|
@@ -395,6 +395,29 @@ function normalizeAllowedModel(model, providerId) {
|
|
|
395
395
|
}
|
|
396
396
|
return s;
|
|
397
397
|
}
|
|
398
|
+
function normalizeEndpointPath(v) {
|
|
399
|
+
const s = String(v || "").trim();
|
|
400
|
+
if (!s) return "";
|
|
401
|
+
return s.startsWith("/") ? s : `/${s}`;
|
|
402
|
+
}
|
|
403
|
+
function defaultModelsPath(id) {
|
|
404
|
+
if (String(id).toLowerCase() === "workbuddy") return "/console/enterprises/personal/models";
|
|
405
|
+
return "/models";
|
|
406
|
+
}
|
|
407
|
+
function defaultChatPath(id) {
|
|
408
|
+
if (String(id).toLowerCase() === "workbuddy") return "/v2/chat/completions";
|
|
409
|
+
return "/chat/completions";
|
|
410
|
+
}
|
|
411
|
+
export function loadProviderModelsPath(id, { file = defaultStateFile() } = {}) {
|
|
412
|
+
const cfg = loadProviderConfigs({ file })[id];
|
|
413
|
+
if (cfg && typeof cfg.modelsPath === "string" && cfg.modelsPath.trim()) return normalizeEndpointPath(cfg.modelsPath);
|
|
414
|
+
return defaultModelsPath(id);
|
|
415
|
+
}
|
|
416
|
+
export function loadProviderChatPath(id, { file = defaultStateFile() } = {}) {
|
|
417
|
+
const cfg = loadProviderConfigs({ file })[id];
|
|
418
|
+
if (cfg && typeof cfg.chatPath === "string" && cfg.chatPath.trim()) return normalizeEndpointPath(cfg.chatPath);
|
|
419
|
+
return defaultChatPath(id);
|
|
420
|
+
}
|
|
398
421
|
|
|
399
422
|
export function loadProviderConfigs({ file = defaultStateFile() } = {}) {
|
|
400
423
|
const v = readState(file).providerConfigs;
|
|
@@ -411,7 +434,9 @@ export function loadProviderConfig(id, { file = defaultStateFile() } = {}) {
|
|
|
411
434
|
const cfg = loadProviderConfigs({ file })[id];
|
|
412
435
|
const allowedModels = cfg && Array.isArray(cfg.allowedModels) ? [...new Set(cfg.allowedModels.map((m) => normalizeAllowedModel(m, id)).filter(Boolean))] : [];
|
|
413
436
|
const auths = cfg && Array.isArray(cfg.auths) ? normalizeAuths(cfg.auths) : [];
|
|
414
|
-
|
|
437
|
+
const modelsPath = cfg && typeof cfg.modelsPath === "string" && cfg.modelsPath.trim() ? normalizeEndpointPath(cfg.modelsPath) : defaultModelsPath(id);
|
|
438
|
+
const chatPath = cfg && typeof cfg.chatPath === "string" && cfg.chatPath.trim() ? normalizeEndpointPath(cfg.chatPath) : defaultChatPath(id);
|
|
439
|
+
if (baseUrl || envKeys.length || allowedModels.length || auths.length) return { baseUrl: normalizeBaseUrl(baseUrl), keys: envKeys, auths, allowedModels, modelsPath, chatPath };
|
|
415
440
|
return null;
|
|
416
441
|
}
|
|
417
442
|
const configs = loadProviderConfigs({ file });
|
|
@@ -422,11 +447,13 @@ export function loadProviderConfig(id, { file = defaultStateFile() } = {}) {
|
|
|
422
447
|
keys: Array.isArray(cfg.keys) ? [...new Set(cfg.keys.filter((x) => typeof x === "string" && x.trim().length))] : [],
|
|
423
448
|
auths: normalizeAuths(cfg.auths),
|
|
424
449
|
allowedModels: Array.isArray(cfg.allowedModels) ? [...new Set(cfg.allowedModels.map((m) => normalizeAllowedModel(m, id)).filter(Boolean))] : [],
|
|
450
|
+
modelsPath: typeof cfg.modelsPath === "string" && cfg.modelsPath.trim() ? normalizeEndpointPath(cfg.modelsPath) : defaultModelsPath(id),
|
|
451
|
+
chatPath: typeof cfg.chatPath === "string" && cfg.chatPath.trim() ? normalizeEndpointPath(cfg.chatPath) : defaultChatPath(id),
|
|
425
452
|
};
|
|
426
453
|
}
|
|
427
454
|
// 兼容旧 providerKeys 形态:有 key 但无 configs 时视为通用供应商(baseUrl 为空,需后补)
|
|
428
455
|
const keys = loadProviderKeys(id, { file });
|
|
429
|
-
if (keys.length) return { baseUrl: String(id).toLowerCase() === "workbuddy" ? WORKBUDDY_DEFAULT_BASE_URL : "", keys, auths: [], allowedModels: [] };
|
|
456
|
+
if (keys.length) return { baseUrl: String(id).toLowerCase() === "workbuddy" ? WORKBUDDY_DEFAULT_BASE_URL : "", keys, auths: [], allowedModels: [], modelsPath: defaultModelsPath(id), chatPath: defaultChatPath(id) };
|
|
430
457
|
return null;
|
|
431
458
|
}
|
|
432
459
|
|
|
@@ -451,12 +478,16 @@ export function saveProviderAuths(id, list, { file = defaultStateFile() } = {})
|
|
|
451
478
|
const baseUrl = normalizeBaseUrl(cur.baseUrl || loadProviderBaseUrl(id, { file }) || "");
|
|
452
479
|
const keys = Array.isArray(cur.keys) ? [...new Set(cur.keys.filter((x) => typeof x === "string" && x.trim().length))] : loadProviderKeys(id, { file });
|
|
453
480
|
const allowedModels = Array.isArray(cur.allowedModels) ? [...new Set(cur.allowedModels.map((m) => normalizeAllowedModel(m, id)).filter(Boolean))] : [];
|
|
454
|
-
|
|
481
|
+
const modelsPath = typeof cur.modelsPath === "string" ? normalizeEndpointPath(cur.modelsPath) : "";
|
|
482
|
+
const chatPath = typeof cur.chatPath === "string" ? normalizeEndpointPath(cur.chatPath) : "";
|
|
483
|
+
if (!baseUrl && !keys.length && !clean.length && !allowedModels.length && !modelsPath && !chatPath) {
|
|
455
484
|
delete configs[id];
|
|
456
485
|
} else {
|
|
457
486
|
configs[id] = { baseUrl, keys };
|
|
458
487
|
if (clean.length) configs[id].auths = clean;
|
|
459
488
|
if (allowedModels.length) configs[id].allowedModels = allowedModels;
|
|
489
|
+
if (modelsPath) configs[id].modelsPath = modelsPath;
|
|
490
|
+
if (chatPath) configs[id].chatPath = chatPath;
|
|
460
491
|
}
|
|
461
492
|
writeStateImmediate(file, { providerConfigs: configs });
|
|
462
493
|
return clean;
|
|
@@ -469,33 +500,43 @@ export function saveProviderBaseUrl(id, baseUrl, { file = defaultStateFile() } =
|
|
|
469
500
|
const keys = Array.isArray(cur.keys) ? cur.keys : loadProviderKeys(id, { file });
|
|
470
501
|
const auths = normalizeAuths(cur.auths);
|
|
471
502
|
const allowedModels = Array.isArray(cur.allowedModels) ? [...new Set(cur.allowedModels.map((m) => normalizeAllowedModel(m, id)).filter(Boolean))] : [];
|
|
472
|
-
|
|
503
|
+
const modelsPath = typeof cur.modelsPath === "string" ? normalizeEndpointPath(cur.modelsPath) : "";
|
|
504
|
+
const chatPath = typeof cur.chatPath === "string" ? normalizeEndpointPath(cur.chatPath) : "";
|
|
505
|
+
if (!clean && !keys.length && !auths.length && !allowedModels.length && !modelsPath && !chatPath) {
|
|
473
506
|
delete configs[id];
|
|
474
507
|
} else {
|
|
475
508
|
configs[id] = { baseUrl: clean, keys };
|
|
476
509
|
if (auths.length) configs[id].auths = auths;
|
|
477
510
|
if (allowedModels.length) configs[id].allowedModels = allowedModels;
|
|
511
|
+
if (modelsPath) configs[id].modelsPath = modelsPath;
|
|
512
|
+
if (chatPath) configs[id].chatPath = chatPath;
|
|
478
513
|
}
|
|
479
514
|
writeStateImmediate(file, { providerConfigs: configs });
|
|
480
515
|
return clean;
|
|
481
516
|
}
|
|
482
517
|
|
|
483
|
-
export function saveProviderConfig(id, { baseUrl, keys, auths, allowedModels }, { file = defaultStateFile() } = {}) {
|
|
518
|
+
export function saveProviderConfig(id, { baseUrl, keys, auths, allowedModels, modelsPath, chatPath }, { file = defaultStateFile() } = {}) {
|
|
484
519
|
const cleanUrl = normalizeBaseUrl(baseUrl);
|
|
485
520
|
const cleanKeys = [...new Set((Array.isArray(keys) ? keys : []).map((k) => String(k || "").trim()).filter(Boolean))];
|
|
486
521
|
const cleanAuths = auths === undefined ? undefined : normalizeAuths(auths);
|
|
487
522
|
const cleanAllowed = [...new Set((Array.isArray(allowedModels) ? allowedModels : []).map((m) => normalizeAllowedModel(m, id)).filter(Boolean))];
|
|
523
|
+
const cleanModelsPath = modelsPath === undefined ? undefined : (String(modelsPath).trim() ? normalizeEndpointPath(modelsPath) : "");
|
|
524
|
+
const cleanChatPath = chatPath === undefined ? undefined : (String(chatPath).trim() ? normalizeEndpointPath(chatPath) : "");
|
|
488
525
|
const configs = { ...loadProviderConfigs({ file }) };
|
|
489
526
|
const cur = configs[id] && typeof configs[id] === "object" ? configs[id] : {};
|
|
490
|
-
// 保留已有的 allowedModels / auths 若本次未传入
|
|
527
|
+
// 保留已有的 allowedModels / auths / paths 若本次未传入
|
|
491
528
|
const finalAllowed = allowedModels === undefined ? (Array.isArray(cur.allowedModels) ? [...new Set(cur.allowedModels.map((m) => normalizeAllowedModel(m, id)).filter(Boolean))] : []) : cleanAllowed;
|
|
492
529
|
const finalAuths = cleanAuths === undefined ? normalizeAuths(cur.auths) : cleanAuths;
|
|
493
|
-
|
|
530
|
+
const finalModelsPath = cleanModelsPath === undefined ? (typeof cur.modelsPath === "string" ? normalizeEndpointPath(cur.modelsPath) : "") : cleanModelsPath;
|
|
531
|
+
const finalChatPath = cleanChatPath === undefined ? (typeof cur.chatPath === "string" ? normalizeEndpointPath(cur.chatPath) : "") : cleanChatPath;
|
|
532
|
+
if (!cleanUrl && !cleanKeys.length && !finalAllowed.length && !finalAuths.length && !finalModelsPath && !finalChatPath) {
|
|
494
533
|
delete configs[id];
|
|
495
534
|
} else {
|
|
496
535
|
configs[id] = { baseUrl: cleanUrl, keys: cleanKeys };
|
|
497
536
|
if (finalAuths.length) configs[id].auths = finalAuths;
|
|
498
537
|
if (finalAllowed.length) configs[id].allowedModels = finalAllowed;
|
|
538
|
+
if (finalModelsPath) configs[id].modelsPath = finalModelsPath;
|
|
539
|
+
if (finalChatPath) configs[id].chatPath = finalChatPath;
|
|
499
540
|
}
|
|
500
541
|
// 同步清理旧 providerKeys 中同 id 的残留,避免双写
|
|
501
542
|
const oldKeys = readState(file).providerKeys;
|
|
@@ -503,10 +544,10 @@ export function saveProviderConfig(id, { baseUrl, keys, auths, allowedModels },
|
|
|
503
544
|
const nextKeys = { ...oldKeys };
|
|
504
545
|
delete nextKeys[id];
|
|
505
546
|
writeStateImmediate(file, { providerKeys: nextKeys, providerConfigs: configs });
|
|
506
|
-
return { baseUrl: cleanUrl, keys: cleanKeys, auths: finalAuths, allowedModels: finalAllowed };
|
|
547
|
+
return { baseUrl: cleanUrl, keys: cleanKeys, auths: finalAuths, allowedModels: finalAllowed, modelsPath: finalModelsPath, chatPath: finalChatPath };
|
|
507
548
|
}
|
|
508
549
|
writeStateImmediate(file, { providerConfigs: configs });
|
|
509
|
-
return { baseUrl: cleanUrl, keys: cleanKeys, auths: finalAuths, allowedModels: finalAllowed };
|
|
550
|
+
return { baseUrl: cleanUrl, keys: cleanKeys, auths: finalAuths, allowedModels: finalAllowed, modelsPath: finalModelsPath, chatPath: finalChatPath };
|
|
510
551
|
}
|
|
511
552
|
|
|
512
553
|
// ---- 供应商模型白名单:providerConfigs.<id>.allowedModels(空 = 不限) ----
|
|
@@ -525,12 +566,16 @@ export function saveProviderAllowedModels(id, list, { file = defaultStateFile()
|
|
|
525
566
|
const baseUrl = normalizeBaseUrl(cur.baseUrl || loadProviderBaseUrl(id, { file }) || "");
|
|
526
567
|
const keys = Array.isArray(cur.keys) ? cur.keys : loadProviderKeys(id, { file });
|
|
527
568
|
const auths = normalizeAuths(cur.auths);
|
|
528
|
-
|
|
569
|
+
const modelsPath = typeof cur.modelsPath === "string" ? normalizeEndpointPath(cur.modelsPath) : "";
|
|
570
|
+
const chatPath = typeof cur.chatPath === "string" ? normalizeEndpointPath(cur.chatPath) : "";
|
|
571
|
+
if (!baseUrl && !keys.length && !auths.length && !clean.length && !modelsPath && !chatPath) {
|
|
529
572
|
delete configs[id];
|
|
530
573
|
} else {
|
|
531
574
|
configs[id] = { baseUrl, keys };
|
|
532
575
|
if (auths.length) configs[id].auths = auths;
|
|
533
576
|
if (clean.length) configs[id].allowedModels = clean;
|
|
577
|
+
if (modelsPath) configs[id].modelsPath = modelsPath;
|
|
578
|
+
if (chatPath) configs[id].chatPath = chatPath;
|
|
534
579
|
}
|
|
535
580
|
writeStateImmediate(file, { providerConfigs: configs });
|
|
536
581
|
return clean;
|