dsh-custom-api 1.0.1 → 1.0.3
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/lib/index.js +224 -41
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -2,10 +2,10 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Plugin:
|
|
5
|
+
* Plugin: dsh-custom-api
|
|
6
6
|
* Tự động phát hiện model từ Custom API (OpenAI/Anthropic compatible),
|
|
7
|
-
* kết hợp API Endpoint Metadata, models.dev và bộ quy chuẩn Reasoning Ladder theo từng hãng (vendor)
|
|
8
|
-
*
|
|
7
|
+
* kết hợp API Endpoint Metadata, models.dev và bộ quy chuẩn Reasoning Ladder theo từng hãng (vendor).
|
|
8
|
+
* Giải quyết triệt để lỗi "không hỗ trợ reasoning effort" khi chuyển mức độ suy nghĩ.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
export const name = 'dsh-custom-api';
|
|
@@ -52,23 +52,18 @@ const THINKING_DESCRIPTION =
|
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
* Reasoning Ladder chuẩn hóa theo từng hãng từ tài liệu API chính thức:
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* - Gemini: per-model (low/medium/high, Flash dòng 3.x có thêm minimal)
|
|
58
|
-
* - Claude: low / medium / high / xhigh / max (platform.claude.com)
|
|
59
|
-
* - Grok: low / medium / high / xhigh (docs.x.ai)
|
|
60
|
-
* - GLM: none / high / max (docs.z.ai)
|
|
61
|
-
* - Qwen: toggle only (off: null, high: high)
|
|
55
|
+
* Bao gồm đầy đủ 'off' và các nấc 'low', 'medium', 'high', 'max' để người dùng
|
|
56
|
+
* chuyển bất kỳ nấc nào trên giao diện cũng KHÔNG bao giờ bị báo lỗi "không hỗ trợ".
|
|
62
57
|
*/
|
|
63
58
|
const FAMILY_EFFORTS = Object.freeze({
|
|
64
|
-
deepseek: { off: 'none', low: 'low', high: 'high', max: 'max' },
|
|
65
|
-
openai: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },
|
|
66
|
-
gemini: { low: 'low', medium: 'medium', high: 'high' },
|
|
67
|
-
claude: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },
|
|
68
|
-
grok: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },
|
|
69
|
-
glm: { off: 'none', high: 'high', max: 'max' },
|
|
70
|
-
qwen: { off: null, high: 'high' },
|
|
71
|
-
other: { off: null, high: 'high' },
|
|
59
|
+
deepseek: { off: 'none', low: 'low', medium: 'high', high: 'high', max: 'max' },
|
|
60
|
+
openai: { off: null, low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },
|
|
61
|
+
gemini: { off: null, minimal: 'minimal', low: 'low', medium: 'medium', high: 'high' },
|
|
62
|
+
claude: { off: null, low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },
|
|
63
|
+
grok: { off: null, low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },
|
|
64
|
+
glm: { off: 'none', low: 'high', medium: 'high', high: 'high', max: 'max' },
|
|
65
|
+
qwen: { off: null, low: 'high', medium: 'high', high: 'high' },
|
|
66
|
+
other: { off: null, low: 'low', medium: 'medium', high: 'high', max: 'max' },
|
|
72
67
|
});
|
|
73
68
|
|
|
74
69
|
const GEMINI_LEVELS = Object.freeze({
|
|
@@ -87,10 +82,13 @@ const GEMINI_LEVELS = Object.freeze({
|
|
|
87
82
|
});
|
|
88
83
|
|
|
89
84
|
function geminiEfforts(id) {
|
|
90
|
-
const norm =
|
|
91
|
-
const
|
|
92
|
-
const
|
|
85
|
+
const norm = normalizeModelId(id);
|
|
86
|
+
const tail = String(id).toLowerCase().split('/').pop() || '';
|
|
87
|
+
const documented = GEMINI_LEVELS[tail] || GEMINI_LEVELS[norm] || ['low', 'medium', 'high'];
|
|
88
|
+
const efforts = { off: null };
|
|
93
89
|
for (const level of documented) efforts[level] = level;
|
|
90
|
+
if (!efforts.medium) efforts.medium = efforts.high || 'medium';
|
|
91
|
+
if (!efforts.low) efforts.low = efforts.medium || 'low';
|
|
94
92
|
return efforts;
|
|
95
93
|
}
|
|
96
94
|
|
|
@@ -149,6 +147,16 @@ function buildModelsDevIndex(data) {
|
|
|
149
147
|
isLoaded = true;
|
|
150
148
|
}
|
|
151
149
|
|
|
150
|
+
// Synchronously load cache on module import if exists
|
|
151
|
+
try {
|
|
152
|
+
if (fs.existsSync(CACHE_PATH)) {
|
|
153
|
+
const content = fs.readFileSync(CACHE_PATH, 'utf8');
|
|
154
|
+
buildModelsDevIndex(JSON.parse(content));
|
|
155
|
+
}
|
|
156
|
+
} catch {
|
|
157
|
+
// Ignore sync read error, will be retried in initModelsDevDatabase
|
|
158
|
+
}
|
|
159
|
+
|
|
152
160
|
async function initModelsDevDatabase(logger) {
|
|
153
161
|
try {
|
|
154
162
|
if (fs.existsSync(CACHE_PATH)) {
|
|
@@ -332,11 +340,22 @@ function determineReasoningEfforts(id, model, dev, forceThinking) {
|
|
|
332
340
|
let isThinking = forceThinking;
|
|
333
341
|
if (!isThinking) {
|
|
334
342
|
if (typeof model?.supports_thinking === 'boolean') isThinking = model.supports_thinking;
|
|
343
|
+
else if (model?.reasoningEfforts && typeof model.reasoningEfforts === 'object') isThinking = true;
|
|
335
344
|
else if (dev?.reasoning === true) isThinking = true;
|
|
336
|
-
else if (THINKING_MODEL_ID.test(String(id))) isThinking = true;
|
|
337
345
|
else {
|
|
338
|
-
const
|
|
339
|
-
isThinking =
|
|
346
|
+
const s = String(id).toLowerCase();
|
|
347
|
+
isThinking = (
|
|
348
|
+
s.includes('r1') || s.includes('o1') || s.includes('o3') || s.includes('o4') ||
|
|
349
|
+
s.includes('reason') || s.includes('think') || s.includes('thought') ||
|
|
350
|
+
s.includes('qwq') || s.includes('qvq') ||
|
|
351
|
+
s.includes('gemini-2.5') || s.includes('gemini-3') || s.includes('gemini-4') ||
|
|
352
|
+
(s.includes('claude') && (s.includes('3-7') || s.includes('3.7') || s.includes('sonnet') || s.includes('opus') || s.includes('4') || s.includes('5'))) ||
|
|
353
|
+
s.includes('gpt-5') || s.includes('gpt-6') ||
|
|
354
|
+
s.includes('grok-4') || s.includes('grok-3') ||
|
|
355
|
+
s.includes('glm-5') || s.includes('deepseek-v4') ||
|
|
356
|
+
s.includes('qwen-3') || s.includes('qwen3') ||
|
|
357
|
+
THINKING_DESCRIPTION.test(typeof model?.description === 'string' ? model.description : '')
|
|
358
|
+
);
|
|
340
359
|
}
|
|
341
360
|
}
|
|
342
361
|
|
|
@@ -392,7 +411,7 @@ function isImageGenerationEndpoint(id, dev) {
|
|
|
392
411
|
/**
|
|
393
412
|
* Giải quyết và gán năng lực hoàn chỉnh cho một model
|
|
394
413
|
*/
|
|
395
|
-
function resolveCapabilities(rawModel, opts = {}) {
|
|
414
|
+
export function resolveCapabilities(rawModel, opts = {}) {
|
|
396
415
|
const id = typeof rawModel === 'string' ? rawModel : rawModel.id;
|
|
397
416
|
const dev = lookupModelsDev(id);
|
|
398
417
|
const apiMeta = extractFromApiMetadata(rawModel);
|
|
@@ -498,12 +517,174 @@ async function fetchModelsFromApi(baseURL, apiKey, apiProtocol = 'openai-complet
|
|
|
498
517
|
throw new Error(`Không thể lấy danh sách model từ ${baseURL}: ${lastError?.message || 'Không tìm thấy models'}`);
|
|
499
518
|
}
|
|
500
519
|
|
|
520
|
+
/**
|
|
521
|
+
* Patch một instance model đã được PiAi resolve để luôn hỗ trợ thinking effort và vision
|
|
522
|
+
*/
|
|
523
|
+
function patchResolvedModel(resolved, modelId) {
|
|
524
|
+
if (!resolved || typeof resolved !== 'object') return;
|
|
525
|
+
const cap = resolveCapabilities(modelId || resolved.id);
|
|
526
|
+
|
|
527
|
+
if (cap.hasThinking) {
|
|
528
|
+
resolved.reasoning = true;
|
|
529
|
+
if (!resolved.thinkingLevelMap || typeof resolved.thinkingLevelMap !== 'object') {
|
|
530
|
+
resolved.thinkingLevelMap = {};
|
|
531
|
+
}
|
|
532
|
+
const efforts = cap.modelEntry.reasoningEfforts || {};
|
|
533
|
+
for (const [lvl, wire] of Object.entries(efforts)) {
|
|
534
|
+
if (wire !== null && wire !== undefined) {
|
|
535
|
+
resolved.thinkingLevelMap[lvl] = wire;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Đảm bảo 'off' luôn được hỗ trợ (không bị null)
|
|
540
|
+
if (resolved.thinkingLevelMap.off === null) {
|
|
541
|
+
delete resolved.thinkingLevelMap.off;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Đảm bảo các nấc tiêu chuẩn (low, medium, high) không bị null
|
|
545
|
+
if (resolved.thinkingLevelMap.high === null || resolved.thinkingLevelMap.high === undefined) {
|
|
546
|
+
resolved.thinkingLevelMap.high = 'high';
|
|
547
|
+
}
|
|
548
|
+
if (resolved.thinkingLevelMap.medium === null || resolved.thinkingLevelMap.medium === undefined) {
|
|
549
|
+
resolved.thinkingLevelMap.medium = resolved.thinkingLevelMap.high || 'medium';
|
|
550
|
+
}
|
|
551
|
+
if (resolved.thinkingLevelMap.low === null || resolved.thinkingLevelMap.low === undefined) {
|
|
552
|
+
resolved.thinkingLevelMap.low = resolved.thinkingLevelMap.medium || 'low';
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (!resolved.compat || typeof resolved.compat !== 'object') {
|
|
556
|
+
resolved.compat = {};
|
|
557
|
+
}
|
|
558
|
+
resolved.compat.supportsReasoningEffort = true;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (cap.hasVision && Array.isArray(resolved.input) && !resolved.input.includes('image')) {
|
|
562
|
+
resolved.input.push('image');
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Tự động quét và nâng cấp tất cả provider hiện có trong llm-pi-ai trên startup
|
|
568
|
+
*/
|
|
569
|
+
async function autoEnhanceStoredProfiles(ctx) {
|
|
570
|
+
if (!ctx.configEditor) return;
|
|
571
|
+
try {
|
|
572
|
+
const entries = ctx.configEditor.entries();
|
|
573
|
+
const entry = entries.find(
|
|
574
|
+
(e) => e.options?.id === 'llm-pi-ai' || e.patchId === 'llm-pi-ai' || e.id === 'include:llm-pi-ai'
|
|
575
|
+
);
|
|
576
|
+
if (!entry) return;
|
|
577
|
+
|
|
578
|
+
await ctx.configEditor.edit(entry, (current = {}) => {
|
|
579
|
+
const next = structuredClone(current);
|
|
580
|
+
if (!next.providers) return next;
|
|
581
|
+
|
|
582
|
+
let hasChanges = false;
|
|
583
|
+
for (const [pid, p] of Object.entries(next.providers)) {
|
|
584
|
+
if (!p || !Array.isArray(p.models)) continue;
|
|
585
|
+
for (const m of p.models) {
|
|
586
|
+
if (!m || typeof m !== 'object') continue;
|
|
587
|
+
const cap = resolveCapabilities(m, { api: p.api });
|
|
588
|
+
|
|
589
|
+
if (cap.hasVision && (!m.input || !m.input.includes('image'))) {
|
|
590
|
+
m.input = ['text', 'image'];
|
|
591
|
+
hasChanges = true;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (cap.hasThinking && (m.reasoningEfforts === undefined || m.reasoningEfforts === false)) {
|
|
595
|
+
m.reasoningEfforts = cap.modelEntry.reasoningEfforts;
|
|
596
|
+
if (p.api === 'openai-completions') {
|
|
597
|
+
const s = (m.id || '').toLowerCase();
|
|
598
|
+
const isDeepSeek = s.includes('deepseek') || s.includes('r1');
|
|
599
|
+
m.compat = {
|
|
600
|
+
...m.compat,
|
|
601
|
+
supportsReasoningEffort: true,
|
|
602
|
+
...(isDeepSeek ? { thinkingFormat: 'deepseek' } : {}),
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
hasChanges = true;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return hasChanges ? next : current;
|
|
610
|
+
});
|
|
611
|
+
} catch (err) {
|
|
612
|
+
ctx.logger?.warn(`autoEnhanceStoredProfiles error: ${err.message}`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
501
616
|
export function apply(ctx) {
|
|
502
617
|
initModelsDevDatabase(ctx.logger);
|
|
503
618
|
ctx.logger?.info('Custom Model Discovery Plugin (API Metadata + models.dev + Vendor Reasoning Ladders) active');
|
|
504
619
|
|
|
505
620
|
// =========================================================================
|
|
506
|
-
// 1.
|
|
621
|
+
// 1. Hook trực tiếp PiAiAdapter.prototype.modelOf
|
|
622
|
+
// Đảm bảo ở cấp Engine Adapter, model luôn có reasoning=true và thinkingLevelMap
|
|
623
|
+
// đầy đủ các mức (off, low, medium, high), triệt tiêu lỗi "không hỗ trợ" khi chuyển mức
|
|
624
|
+
// =========================================================================
|
|
625
|
+
(async () => {
|
|
626
|
+
let PiAiAdapterClass = null;
|
|
627
|
+
try {
|
|
628
|
+
const mod = await import('/usr/local/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/dsh-llm-pi-ai/lib/index.js');
|
|
629
|
+
PiAiAdapterClass = mod.PiAiAdapter;
|
|
630
|
+
} catch {
|
|
631
|
+
if (ctx.llm?.adapters) {
|
|
632
|
+
for (const reg of ctx.llm.adapters.values()) {
|
|
633
|
+
if (reg?.adapter?.constructor?.name === 'PiAiAdapter') {
|
|
634
|
+
PiAiAdapterClass = reg.adapter.constructor;
|
|
635
|
+
break;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (PiAiAdapterClass && typeof PiAiAdapterClass.prototype?.modelOf === 'function') {
|
|
642
|
+
const originalModelOf = PiAiAdapterClass.prototype.modelOf;
|
|
643
|
+
if (!originalModelOf.__dshEnhanced) {
|
|
644
|
+
const patched = function (snapshot, provider, model) {
|
|
645
|
+
const resolved = originalModelOf.call(this, snapshot, provider, model);
|
|
646
|
+
if (resolved) {
|
|
647
|
+
patchResolvedModel(resolved, model);
|
|
648
|
+
}
|
|
649
|
+
return resolved;
|
|
650
|
+
};
|
|
651
|
+
patched.__dshEnhanced = true;
|
|
652
|
+
PiAiAdapterClass.prototype.modelOf = patched;
|
|
653
|
+
ctx.logger?.info('PiAiAdapter.prototype.modelOf successfully hooked with dynamic reasoning & vision enhancer');
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
})();
|
|
657
|
+
|
|
658
|
+
// =========================================================================
|
|
659
|
+
// 2. Live Hook vào ctx.llm.resolveCallConfig
|
|
660
|
+
// Bảo vệ lớp LlmRuntime: nếu có yêu cầu reasoningEffort hợp lệ, không bao giờ để ném UNSUPPORTED_REASONING_EFFORT
|
|
661
|
+
// =========================================================================
|
|
662
|
+
if (ctx.llm && typeof ctx.llm.resolveCallConfig === 'function') {
|
|
663
|
+
const originalResolveCallConfig = ctx.llm.resolveCallConfig.bind(ctx.llm);
|
|
664
|
+
ctx.llm.resolveCallConfig = async function (config, signal) {
|
|
665
|
+
try {
|
|
666
|
+
return await originalResolveCallConfig(config, signal);
|
|
667
|
+
} catch (err) {
|
|
668
|
+
if (err && err.code === 'UNSUPPORTED_REASONING_EFFORT') {
|
|
669
|
+
const cap = resolveCapabilities(config.model);
|
|
670
|
+
if (cap.hasThinking) {
|
|
671
|
+
ctx.logger?.info(`resolveCallConfig: gracefully accepted effort "${config.reasoningEffort}" for ${config.provider}/${config.model}`);
|
|
672
|
+
return {
|
|
673
|
+
...config,
|
|
674
|
+
reasoningEffort: config.reasoningEffort,
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
throw err;
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
ctx.effect(() => () => {
|
|
682
|
+
ctx.llm.resolveCallConfig = originalResolveCallConfig;
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// =========================================================================
|
|
687
|
+
// 3. Live Hook vào ctx.llm.discoverModels (Web UI Settings -> Models)
|
|
507
688
|
// =========================================================================
|
|
508
689
|
if (ctx.llm && typeof ctx.llm.discoverModels === 'function') {
|
|
509
690
|
const originalDiscoverModels = ctx.llm.discoverModels.bind(ctx.llm);
|
|
@@ -527,7 +708,7 @@ export function apply(ctx) {
|
|
|
527
708
|
}
|
|
528
709
|
|
|
529
710
|
// =========================================================================
|
|
530
|
-
//
|
|
711
|
+
// 4. Live Hook vào ctx.llm.resolveModelInfo
|
|
531
712
|
// =========================================================================
|
|
532
713
|
if (ctx.llm && typeof ctx.llm.resolveModelInfo === 'function') {
|
|
533
714
|
const originalResolveModelInfo = ctx.llm.resolveModelInfo.bind(ctx.llm);
|
|
@@ -538,13 +719,12 @@ export function apply(ctx) {
|
|
|
538
719
|
const cap = resolveCapabilities(model);
|
|
539
720
|
|
|
540
721
|
if (cap.hasThinking && !info.reasoning) {
|
|
722
|
+
const efforts = cap.modelEntry.reasoningEfforts || {};
|
|
541
723
|
info.reasoning = {
|
|
542
|
-
efforts: Object.keys(
|
|
543
|
-
|
|
544
|
-
.
|
|
545
|
-
|
|
546
|
-
name: lvl.charAt(0).toUpperCase() + lvl.slice(1),
|
|
547
|
-
})),
|
|
724
|
+
efforts: Object.keys(efforts).map((lvl) => ({
|
|
725
|
+
id: lvl,
|
|
726
|
+
name: lvl.charAt(0).toUpperCase() + lvl.slice(1),
|
|
727
|
+
})),
|
|
548
728
|
defaultEffort: 'medium',
|
|
549
729
|
};
|
|
550
730
|
}
|
|
@@ -562,8 +742,13 @@ export function apply(ctx) {
|
|
|
562
742
|
});
|
|
563
743
|
}
|
|
564
744
|
|
|
745
|
+
// Tự động nâng cấp cấu hình trong llm-pi-ai sau khi host khởi tạo
|
|
746
|
+
setTimeout(() => {
|
|
747
|
+
autoEnhanceStoredProfiles(ctx);
|
|
748
|
+
}, 1000);
|
|
749
|
+
|
|
565
750
|
// =========================================================================
|
|
566
|
-
//
|
|
751
|
+
// 5. Hàm lưu cấu hình vào DSH giữ nguyên compat tùy biến (preserveCompat)
|
|
567
752
|
// =========================================================================
|
|
568
753
|
async function updateDshProviderConfig(providerId, providerData) {
|
|
569
754
|
if (!ctx.configEditor) {
|
|
@@ -581,7 +766,6 @@ export function apply(ctx) {
|
|
|
581
766
|
if (!next.providers) next.providers = {};
|
|
582
767
|
const existing = next.providers[providerId] || {};
|
|
583
768
|
|
|
584
|
-
// Giữ lại compat tùy biến của từng model cũ nếu có
|
|
585
769
|
const existingCompat = new Map();
|
|
586
770
|
for (const m of existing.models || []) {
|
|
587
771
|
if (m && m.id && m.compat) existingCompat.set(m.id, m.compat);
|
|
@@ -608,7 +792,7 @@ export function apply(ctx) {
|
|
|
608
792
|
}
|
|
609
793
|
|
|
610
794
|
// =========================================================================
|
|
611
|
-
//
|
|
795
|
+
// 6. Tool: discover_custom_models
|
|
612
796
|
// =========================================================================
|
|
613
797
|
if (ctx.tools && typeof ctx.tools.register === 'function') {
|
|
614
798
|
ctx.tools.register({
|
|
@@ -690,7 +874,6 @@ export function apply(ctx) {
|
|
|
690
874
|
api,
|
|
691
875
|
});
|
|
692
876
|
|
|
693
|
-
// Lọc bỏ endpoint thuần sinh ảnh (Flux/Midjourney/DALL-E) khỏi chat catalog
|
|
694
877
|
if (cap.isImageEndpoint) continue;
|
|
695
878
|
|
|
696
879
|
if (cap.hasVision) visionCount++;
|
|
@@ -745,7 +928,7 @@ export function apply(ctx) {
|
|
|
745
928
|
});
|
|
746
929
|
|
|
747
930
|
// =========================================================================
|
|
748
|
-
//
|
|
931
|
+
// 7. Tool: enhance_existing_providers
|
|
749
932
|
// =========================================================================
|
|
750
933
|
ctx.tools.register({
|
|
751
934
|
name: 'enhance_existing_providers',
|
|
@@ -815,10 +998,10 @@ export function apply(ctx) {
|
|
|
815
998
|
maxTokens: Math.min(m.maxTokens || cap.modelEntry.maxTokens, m.contextWindow || cap.modelEntry.contextWindow),
|
|
816
999
|
};
|
|
817
1000
|
|
|
818
|
-
if (cap.hasThinking
|
|
1001
|
+
if (cap.hasThinking) {
|
|
819
1002
|
merged.reasoningEfforts = cap.modelEntry.reasoningEfforts;
|
|
820
1003
|
if (p.api === 'openai-completions') {
|
|
821
|
-
const s = m.id.toLowerCase();
|
|
1004
|
+
const s = (m.id || '').toLowerCase();
|
|
822
1005
|
const isDeepSeek = s.includes('deepseek') || s.includes('r1');
|
|
823
1006
|
merged.compat = {
|
|
824
1007
|
...merged.compat,
|
|
@@ -854,7 +1037,7 @@ export function apply(ctx) {
|
|
|
854
1037
|
}
|
|
855
1038
|
|
|
856
1039
|
// =========================================================================
|
|
857
|
-
//
|
|
1040
|
+
// 8. Slash Commands: /discover-models và /enhance-models
|
|
858
1041
|
// =========================================================================
|
|
859
1042
|
if (ctx.commands && typeof ctx.commands.register === 'function') {
|
|
860
1043
|
ctx.commands.register({
|
package/package.json
CHANGED