@parallel-cli/parallel 0.4.1 → 0.4.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/CHANGELOG.md +116 -0
- package/README.md +136 -131
- package/dist/commands.js +17 -4
- package/dist/config.js +218 -63
- package/dist/controller.js +13 -11
- package/dist/i18n.js +64 -20
- package/dist/index.js +5 -2
- package/dist/pricing.js +162 -54
- package/dist/ui/App.js +141 -56
- package/dist/ui/CommandInput.js +42 -17
- package/dist/ui/SettingsPanel.js +153 -31
- package/dist/ui/Wizard.js +33 -3
- package/dist/ui/views.js +15 -6
- package/package.json +10 -1
package/dist/config.js
CHANGED
|
@@ -13,131 +13,250 @@ export function configFile() {
|
|
|
13
13
|
}
|
|
14
14
|
export const DEEPSEEK_PROVIDER = {
|
|
15
15
|
name: 'DeepSeek',
|
|
16
|
-
baseUrl: 'https://api.deepseek.com',
|
|
16
|
+
baseUrl: 'https://api.deepseek.com/v1',
|
|
17
17
|
apiKey: '',
|
|
18
|
-
models: ['deepseek-v4-
|
|
19
|
-
defaultModel: 'deepseek-v4-
|
|
18
|
+
models: ['deepseek-v4-pro', 'deepseek-v4-flash', 'deepseek-chat', 'deepseek-reasoner'],
|
|
19
|
+
defaultModel: 'deepseek-v4-pro',
|
|
20
20
|
};
|
|
21
21
|
export const PROVIDER_PRESETS = [
|
|
22
|
+
// ── 🇺🇸 Western ──
|
|
22
23
|
{
|
|
23
24
|
name: 'OpenAI',
|
|
24
25
|
baseUrl: 'https://api.openai.com/v1',
|
|
25
26
|
apiKey: '',
|
|
26
|
-
models: ['gpt-4o', 'gpt-4o-mini', 'o4-mini', '
|
|
27
|
-
defaultModel: 'gpt-
|
|
27
|
+
models: ['gpt-5.5', 'gpt-5.5-pro', 'gpt-5.4', 'gpt-5.3-codex', 'gpt-4o', 'gpt-4o-mini', 'o4-mini', 'o3', 'o3-mini', 'o1', 'o1-mini'],
|
|
28
|
+
defaultModel: 'gpt-5.5',
|
|
29
|
+
category: 'western',
|
|
28
30
|
},
|
|
29
|
-
DEEPSEEK_PROVIDER,
|
|
30
31
|
{
|
|
31
32
|
name: 'Anthropic',
|
|
32
|
-
baseUrl: 'https://api.anthropic.com/v1
|
|
33
|
+
baseUrl: 'https://api.anthropic.com/v1',
|
|
33
34
|
apiKey: '',
|
|
34
|
-
models: ['claude-sonnet-4-6', 'claude-
|
|
35
|
+
models: ['claude-opus-4-8', 'claude-opus-4-7', 'claude-sonnet-4-6', 'claude-haiku-4-5'],
|
|
35
36
|
defaultModel: 'claude-sonnet-4-6',
|
|
37
|
+
category: 'western',
|
|
36
38
|
},
|
|
37
39
|
{
|
|
38
|
-
name: '
|
|
39
|
-
baseUrl: 'https://
|
|
40
|
+
name: 'Google Gemini',
|
|
41
|
+
baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
|
|
40
42
|
apiKey: '',
|
|
41
|
-
models: ['
|
|
42
|
-
defaultModel: '
|
|
43
|
+
models: ['gemini-3.1-pro', 'gemini-3.5-flash', 'gemini-3-flash', 'gemini-3.1-flash-lite'],
|
|
44
|
+
defaultModel: 'gemini-3.5-flash',
|
|
45
|
+
category: 'western',
|
|
43
46
|
},
|
|
44
47
|
{
|
|
45
|
-
name: '
|
|
46
|
-
baseUrl: 'https://
|
|
48
|
+
name: 'xAI Grok',
|
|
49
|
+
baseUrl: 'https://api.x.ai/v1',
|
|
47
50
|
apiKey: '',
|
|
48
|
-
models: ['
|
|
49
|
-
defaultModel: '
|
|
51
|
+
models: ['grok-4', 'grok-4-fast-reasoning', 'grok-3', 'grok-code-fast-1'],
|
|
52
|
+
defaultModel: 'grok-4',
|
|
53
|
+
category: 'western',
|
|
50
54
|
},
|
|
51
55
|
{
|
|
52
56
|
name: 'Mistral',
|
|
53
57
|
baseUrl: 'https://api.mistral.ai/v1',
|
|
54
58
|
apiKey: '',
|
|
55
|
-
models: ['mistral-large-
|
|
56
|
-
defaultModel: 'mistral-large-
|
|
59
|
+
models: ['mistral-large-2', 'magistral-medium', 'codestral-latest', 'mistral-small-latest'],
|
|
60
|
+
defaultModel: 'mistral-large-2',
|
|
61
|
+
category: 'western',
|
|
57
62
|
},
|
|
58
63
|
{
|
|
59
|
-
name: '
|
|
60
|
-
baseUrl: 'https://api.
|
|
64
|
+
name: 'Cohere',
|
|
65
|
+
baseUrl: 'https://api.cohere.com/v1',
|
|
61
66
|
apiKey: '',
|
|
62
|
-
models: ['
|
|
63
|
-
defaultModel: '
|
|
67
|
+
models: ['command-a', 'command-r-plus'],
|
|
68
|
+
defaultModel: 'command-a',
|
|
69
|
+
category: 'western',
|
|
64
70
|
},
|
|
65
71
|
{
|
|
66
|
-
name: '
|
|
67
|
-
baseUrl: 'https://api.
|
|
72
|
+
name: 'Perplexity',
|
|
73
|
+
baseUrl: 'https://api.perplexity.ai',
|
|
68
74
|
apiKey: '',
|
|
69
|
-
models: ['
|
|
70
|
-
defaultModel: '
|
|
75
|
+
models: ['sonar-pro', 'sonar-deep-research'],
|
|
76
|
+
defaultModel: 'sonar-pro',
|
|
77
|
+
category: 'western',
|
|
71
78
|
},
|
|
79
|
+
// ── 🇨🇳 Chinese ──
|
|
72
80
|
{
|
|
73
|
-
name: '
|
|
74
|
-
baseUrl: 'https://api.
|
|
81
|
+
name: 'DeepSeek',
|
|
82
|
+
baseUrl: 'https://api.deepseek.com/v1',
|
|
75
83
|
apiKey: '',
|
|
76
|
-
models: ['
|
|
77
|
-
defaultModel: '
|
|
84
|
+
models: ['deepseek-v4-pro', 'deepseek-v4-flash', 'deepseek-chat', 'deepseek-reasoner'],
|
|
85
|
+
defaultModel: 'deepseek-v4-pro',
|
|
86
|
+
category: 'chinese',
|
|
78
87
|
},
|
|
79
88
|
{
|
|
80
|
-
name: '
|
|
81
|
-
baseUrl: 'https://api.
|
|
89
|
+
name: 'MiniMax',
|
|
90
|
+
baseUrl: 'https://api.minimax.io/v1',
|
|
82
91
|
apiKey: '',
|
|
83
|
-
models: ['
|
|
84
|
-
defaultModel: '
|
|
92
|
+
models: ['MiniMax-M3', 'MiniMax-M2.7', 'MiniMax-M2.7-highspeed'],
|
|
93
|
+
defaultModel: 'MiniMax-M3',
|
|
94
|
+
category: 'chinese',
|
|
85
95
|
},
|
|
86
96
|
{
|
|
87
|
-
name: '
|
|
88
|
-
baseUrl: 'https://
|
|
97
|
+
name: 'Z.ai / GLM',
|
|
98
|
+
baseUrl: 'https://open.bigmodel.cn/api/paas/v4',
|
|
89
99
|
apiKey: '',
|
|
90
|
-
models: ['
|
|
91
|
-
defaultModel: '
|
|
100
|
+
models: ['glm-5.2', 'glm-5.1', 'glm-4.7', 'glm-4.7-flash', 'glm-5v-turbo'],
|
|
101
|
+
defaultModel: 'glm-5.2',
|
|
102
|
+
category: 'chinese',
|
|
92
103
|
},
|
|
93
104
|
{
|
|
94
|
-
name: '
|
|
95
|
-
baseUrl: 'https://
|
|
105
|
+
name: 'Alibaba / Qwen',
|
|
106
|
+
baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
|
96
107
|
apiKey: '',
|
|
97
|
-
models: ['
|
|
98
|
-
defaultModel: '
|
|
108
|
+
models: ['qwen3.7-max', 'qwen3.6-max-preview', 'qwen3.6-plus', 'qwen3.5-coder'],
|
|
109
|
+
defaultModel: 'qwen3.7-max',
|
|
110
|
+
category: 'chinese',
|
|
99
111
|
},
|
|
100
112
|
{
|
|
101
|
-
name: '
|
|
102
|
-
baseUrl: 'https://api.
|
|
113
|
+
name: 'Moonshot / Kimi',
|
|
114
|
+
baseUrl: 'https://api.moonshot.cn/v1',
|
|
115
|
+
apiKey: '',
|
|
116
|
+
models: ['kimi-k2.6', 'kimi-k2.7-code', 'kimi-k2.5', 'moonshot-v1-128k'],
|
|
117
|
+
defaultModel: 'kimi-k2.6',
|
|
118
|
+
category: 'chinese',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'Xiaomi / MiMo',
|
|
122
|
+
baseUrl: 'https://api.minimaxi.com/v1',
|
|
123
|
+
apiKey: '',
|
|
124
|
+
models: ['mimo-v2-pro', 'mimo-v2-omni'],
|
|
125
|
+
defaultModel: 'mimo-v2-pro',
|
|
126
|
+
category: 'chinese',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'StepFun',
|
|
130
|
+
baseUrl: 'https://api.stepfun.com/v1',
|
|
103
131
|
apiKey: '',
|
|
104
|
-
models: ['
|
|
105
|
-
defaultModel: '
|
|
132
|
+
models: ['step-2-16k'],
|
|
133
|
+
defaultModel: 'step-2-16k',
|
|
134
|
+
category: 'chinese',
|
|
135
|
+
},
|
|
136
|
+
// ── 🌐 Gateways ──
|
|
137
|
+
{
|
|
138
|
+
name: 'OpenRouter',
|
|
139
|
+
baseUrl: 'https://openrouter.ai/api/v1',
|
|
140
|
+
apiKey: '',
|
|
141
|
+
models: ['openai/gpt-5.5', 'anthropic/claude-sonnet-4-6', 'google/gemini-3.5-flash', 'deepseek/deepseek-v4-pro', 'meta-llama/llama-4-maverick', 'mistralai/mistral-large-2'],
|
|
142
|
+
defaultModel: 'openai/gpt-5.5',
|
|
143
|
+
category: 'gateways',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: 'SiliconFlow',
|
|
147
|
+
baseUrl: 'https://api.siliconflow.cn/v1',
|
|
148
|
+
apiKey: '',
|
|
149
|
+
models: ['deepseek-ai/DeepSeek-V4-Pro', 'deepseek-ai/DeepSeek-R1', 'Qwen/Qwen3-Coder-480B', 'glm-4/GLM-5.2', 'moonshotai/Kimi-K2.6'],
|
|
150
|
+
defaultModel: 'deepseek-ai/DeepSeek-V4-Pro',
|
|
151
|
+
category: 'gateways',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: 'Atlas Cloud',
|
|
155
|
+
baseUrl: 'https://api.atlascloud.ai/v1',
|
|
156
|
+
apiKey: '',
|
|
157
|
+
models: ['deepseek-v4-pro', 'deepseek-r1', 'qwen3.7-max', 'glm-5.2', 'kimi-k2.6', 'llama-4-maverick'],
|
|
158
|
+
defaultModel: 'deepseek-v4-pro',
|
|
159
|
+
category: 'gateways',
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'Requesty',
|
|
163
|
+
baseUrl: 'https://api.requesty.ai/api/v1',
|
|
164
|
+
apiKey: '',
|
|
165
|
+
models: ['gpt-5.5', 'claude-sonnet-4-6', 'gemini-3.5-flash', 'deepseek-v4-pro', 'llama-4-maverick', 'mistral-large-2'],
|
|
166
|
+
defaultModel: 'gpt-5.5',
|
|
167
|
+
category: 'gateways',
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'Vercel AI Gateway',
|
|
171
|
+
baseUrl: 'https://api.vercel.ai/v1',
|
|
172
|
+
apiKey: '',
|
|
173
|
+
models: ['gpt-5.5', 'claude-sonnet-4-6', 'gemini-3.5-flash', 'deepseek-v4-pro', 'llama-4-maverick'],
|
|
174
|
+
defaultModel: 'gpt-5.5',
|
|
175
|
+
category: 'gateways',
|
|
176
|
+
},
|
|
177
|
+
// ── ⚡ Inference ──
|
|
178
|
+
{
|
|
179
|
+
name: 'Groq',
|
|
180
|
+
baseUrl: 'https://api.groq.com/openai/v1',
|
|
181
|
+
apiKey: '',
|
|
182
|
+
models: ['qwen-2.5-coder-32b', 'deepseek-r1-distill-llama-70b', 'kimi-k2.6', 'llama-3.3-70b-versatile'],
|
|
183
|
+
defaultModel: 'qwen-2.5-coder-32b',
|
|
184
|
+
category: 'inference',
|
|
106
185
|
},
|
|
107
186
|
{
|
|
108
187
|
name: 'Cerebras',
|
|
109
188
|
baseUrl: 'https://api.cerebras.ai/v1',
|
|
110
189
|
apiKey: '',
|
|
111
|
-
models: ['llama-
|
|
112
|
-
defaultModel: 'llama-
|
|
190
|
+
models: ['llama-4-maverick-17b-128e-instruct', 'qwen3-coder-480b', 'kimi-k2.6', 'llama-3.3-70b'],
|
|
191
|
+
defaultModel: 'llama-4-maverick-17b-128e-instruct',
|
|
192
|
+
category: 'inference',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'Together AI',
|
|
196
|
+
baseUrl: 'https://api.together.xyz/v1',
|
|
197
|
+
apiKey: '',
|
|
198
|
+
models: ['meta-llama/Llama-4-Maverick-17B-128E-Instruct', 'deepseek-ai/DeepSeek-V3', 'Qwen/Qwen3-Coder-480B', 'moonshotai/Kimi-K2.6'],
|
|
199
|
+
defaultModel: 'meta-llama/Llama-4-Maverick-17B-128E-Instruct',
|
|
200
|
+
category: 'inference',
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: 'Fireworks',
|
|
204
|
+
baseUrl: 'https://api.fireworks.ai/inference/v1',
|
|
205
|
+
apiKey: '',
|
|
206
|
+
models: ['accounts/fireworks/models/llama4-maverick-17b', 'accounts/fireworks/models/deepseek-v3', 'accounts/fireworks/models/qwen3-coder-480b', 'accounts/fireworks/models/kimi-k2.6'],
|
|
207
|
+
defaultModel: 'accounts/fireworks/models/llama4-maverick-17b',
|
|
208
|
+
category: 'inference',
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
name: 'DeepInfra',
|
|
212
|
+
baseUrl: 'https://api.deepinfra.com/v1/openai',
|
|
213
|
+
apiKey: '',
|
|
214
|
+
models: ['meta-llama/Llama-4-Maverick-17B-128E', 'deepseek-ai/DeepSeek-V3', 'Qwen/Qwen3-Coder-480B', 'moonshotai/Kimi-K2.6'],
|
|
215
|
+
defaultModel: 'meta-llama/Llama-4-Maverick-17B-128E',
|
|
216
|
+
category: 'inference',
|
|
113
217
|
},
|
|
114
218
|
{
|
|
115
219
|
name: 'Novita',
|
|
116
220
|
baseUrl: 'https://api.novita.ai/v3/openai',
|
|
117
221
|
apiKey: '',
|
|
118
|
-
models: ['
|
|
119
|
-
defaultModel: '
|
|
222
|
+
models: ['meta-llama/llama-4-maverick-17b-128e', 'deepseek/deepseek-v3', 'qwen/qwen3-coder-480b', 'moonshotai/kimi-k2.6'],
|
|
223
|
+
defaultModel: 'meta-llama/llama-4-maverick-17b-128e',
|
|
224
|
+
category: 'inference',
|
|
120
225
|
},
|
|
121
226
|
{
|
|
122
227
|
name: 'Hyperbolic',
|
|
123
|
-
baseUrl: 'https://api.hyperbolic.
|
|
228
|
+
baseUrl: 'https://api.hyperbolic.ai/v1',
|
|
124
229
|
apiKey: '',
|
|
125
|
-
models: ['deepseek-
|
|
126
|
-
defaultModel: '
|
|
230
|
+
models: ['meta-llama/Llama-4-Maverick-17B-128E', 'deepseek-ai/DeepSeek-V3', 'Qwen/Qwen3-Coder-480B', 'moonshotai/Kimi-K2.6'],
|
|
231
|
+
defaultModel: 'meta-llama/Llama-4-Maverick-17B-128E',
|
|
232
|
+
category: 'inference',
|
|
127
233
|
},
|
|
128
234
|
{
|
|
129
235
|
name: 'SambaNova',
|
|
130
236
|
baseUrl: 'https://api.sambanova.ai/v1',
|
|
131
237
|
apiKey: '',
|
|
132
|
-
models: ['
|
|
133
|
-
defaultModel: '
|
|
238
|
+
models: ['Meta-Llama-4-Maverick-17B-128E-Instruct', 'DeepSeek-V3', 'Llama-3.3-70B-Instruct'],
|
|
239
|
+
defaultModel: 'Meta-Llama-4-Maverick-17B-128E-Instruct',
|
|
240
|
+
category: 'inference',
|
|
134
241
|
},
|
|
242
|
+
// ── 🏠 Local ──
|
|
135
243
|
{
|
|
136
244
|
name: 'Ollama',
|
|
137
245
|
baseUrl: 'http://localhost:11434/v1',
|
|
246
|
+
apiKey: 'ollama-local',
|
|
247
|
+
models: ['qwen3-coder:480b', 'glm-4.7', 'deepseek-v3', 'kimi-k2', 'llama3.2', 'mistral', 'codellama', 'gemma3'],
|
|
248
|
+
defaultModel: 'qwen3-coder:480b',
|
|
249
|
+
category: 'local',
|
|
250
|
+
requiresApiKey: false,
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
name: 'vLLM / SGLang',
|
|
254
|
+
baseUrl: 'http://localhost:8000/v1',
|
|
138
255
|
apiKey: '',
|
|
139
|
-
models: ['
|
|
140
|
-
defaultModel: '
|
|
256
|
+
models: ['your-model-here'],
|
|
257
|
+
defaultModel: '',
|
|
258
|
+
category: 'local',
|
|
259
|
+
requiresApiKey: false,
|
|
141
260
|
},
|
|
142
261
|
];
|
|
143
262
|
export const DEFAULTS = {
|
|
@@ -155,6 +274,15 @@ function normalizeApprovalMode(mode) {
|
|
|
155
274
|
return 'auto-safe';
|
|
156
275
|
return 'ask';
|
|
157
276
|
}
|
|
277
|
+
export function isLocalProvider(p) {
|
|
278
|
+
return /localhost|127\.0\.0\.1|0\.0\.0\.0/.test(p.baseUrl);
|
|
279
|
+
}
|
|
280
|
+
export function providerNeedsApiKey(p) {
|
|
281
|
+
return p.requiresApiKey !== false && !isLocalProvider(p);
|
|
282
|
+
}
|
|
283
|
+
export function providerReady(p) {
|
|
284
|
+
return !providerNeedsApiKey(p) || p.apiKey.trim().length > 0;
|
|
285
|
+
}
|
|
158
286
|
export function getProvider(cfg, name) {
|
|
159
287
|
const n = (name ?? cfg.defaultProvider).toLowerCase();
|
|
160
288
|
return cfg.providers.find((p) => p.name.toLowerCase() === n) ?? (name ? undefined : cfg.providers[0]);
|
|
@@ -185,6 +313,20 @@ function migrate(raw, cfg) {
|
|
|
185
313
|
cfg.providers = [p];
|
|
186
314
|
cfg.defaultProvider = p.name;
|
|
187
315
|
}
|
|
316
|
+
function normalizeConfig(cfg) {
|
|
317
|
+
cfg.providers = cfg.providers.filter((p) => p && typeof p.name === 'string' && typeof p.baseUrl === 'string');
|
|
318
|
+
for (const p of cfg.providers) {
|
|
319
|
+
if (!Array.isArray(p.models))
|
|
320
|
+
p.models = [];
|
|
321
|
+
if (typeof p.defaultModel !== 'string')
|
|
322
|
+
p.defaultModel = p.models[0] ?? '';
|
|
323
|
+
if (typeof p.apiKey !== 'string')
|
|
324
|
+
p.apiKey = '';
|
|
325
|
+
}
|
|
326
|
+
const defaultExists = cfg.providers.some((p) => p.name.toLowerCase() === cfg.defaultProvider.toLowerCase());
|
|
327
|
+
if (!defaultExists)
|
|
328
|
+
cfg.defaultProvider = cfg.providers[0]?.name ?? '';
|
|
329
|
+
}
|
|
188
330
|
export function loadConfig() {
|
|
189
331
|
let cfg = { ...DEFAULTS, providers: [] };
|
|
190
332
|
try {
|
|
@@ -201,17 +343,30 @@ export function loadConfig() {
|
|
|
201
343
|
catch {
|
|
202
344
|
// ignore corrupted config
|
|
203
345
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
346
|
+
normalizeConfig(cfg);
|
|
347
|
+
// Env vars: PARALLEL_API_KEY targets the current default provider; DEEPSEEK_API_KEY only targets DeepSeek.
|
|
348
|
+
const parallelKey = process.env.PARALLEL_API_KEY;
|
|
349
|
+
const deepseekKey = process.env.DEEPSEEK_API_KEY;
|
|
350
|
+
if (parallelKey && cfg.providers.length === 0) {
|
|
351
|
+
cfg.providers = [{ ...DEEPSEEK_PROVIDER, apiKey: parallelKey }];
|
|
208
352
|
cfg.defaultProvider = DEEPSEEK_PROVIDER.name;
|
|
209
353
|
}
|
|
210
|
-
else if (
|
|
354
|
+
else if (parallelKey) {
|
|
211
355
|
const p = getProvider(cfg);
|
|
212
356
|
if (p)
|
|
213
|
-
p.apiKey =
|
|
357
|
+
p.apiKey = parallelKey;
|
|
358
|
+
}
|
|
359
|
+
if (deepseekKey) {
|
|
360
|
+
const p = getProvider(cfg, DEEPSEEK_PROVIDER.name);
|
|
361
|
+
if (p)
|
|
362
|
+
p.apiKey = deepseekKey;
|
|
363
|
+
else {
|
|
364
|
+
cfg.providers.push({ ...DEEPSEEK_PROVIDER, apiKey: deepseekKey });
|
|
365
|
+
if (!cfg.defaultProvider)
|
|
366
|
+
cfg.defaultProvider = DEEPSEEK_PROVIDER.name;
|
|
367
|
+
}
|
|
214
368
|
}
|
|
369
|
+
normalizeConfig(cfg);
|
|
215
370
|
const p = getProvider(cfg);
|
|
216
371
|
if (p) {
|
|
217
372
|
if (process.env.PARALLEL_BASE_URL)
|
package/dist/controller.js
CHANGED
|
@@ -112,17 +112,19 @@ export class Controller extends EventEmitter {
|
|
|
112
112
|
}
|
|
113
113
|
/** Resolve "model" or "provider:model" against the configured providers. */
|
|
114
114
|
resolveModel(spec) {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
const trimmed = spec.trim();
|
|
116
|
+
const sep = trimmed.indexOf(':');
|
|
117
|
+
if (sep > 0) {
|
|
118
|
+
const provider = getProvider(this.config, trimmed.slice(0, sep).trim());
|
|
119
|
+
if (provider)
|
|
120
|
+
return { provider, model: trimmed.slice(sep + 1).trim() };
|
|
119
121
|
}
|
|
120
122
|
// bare model name: current session provider first, then any provider listing it
|
|
121
123
|
const cur = this.sessionProvider();
|
|
122
124
|
if (cur)
|
|
123
|
-
return { provider: cur, model:
|
|
124
|
-
const any = this.config.providers.find((p) => p.models.includes(
|
|
125
|
-
return any ? { provider: any, model:
|
|
125
|
+
return { provider: cur, model: trimmed };
|
|
126
|
+
const any = this.config.providers.find((p) => p.models.includes(trimmed));
|
|
127
|
+
return any ? { provider: any, model: trimmed } : null;
|
|
126
128
|
}
|
|
127
129
|
llmFor(provider, model) {
|
|
128
130
|
const key = JSON.stringify([provider.name, provider.baseUrl, provider.apiKey, model]);
|
|
@@ -670,7 +672,7 @@ export class Controller extends EventEmitter {
|
|
|
670
672
|
if (!p)
|
|
671
673
|
return false;
|
|
672
674
|
this.session.providerName = p.name;
|
|
673
|
-
this.session.model = p.defaultModel;
|
|
675
|
+
this.session.model = p.defaultModel || p.models[0] || '';
|
|
674
676
|
this.emit('update');
|
|
675
677
|
return true;
|
|
676
678
|
}
|
|
@@ -690,11 +692,11 @@ export class Controller extends EventEmitter {
|
|
|
690
692
|
if (this.session.providerName.toLowerCase() === p.name.toLowerCase()) {
|
|
691
693
|
this.session.providerName = p.name;
|
|
692
694
|
if (!p.models.includes(this.session.model))
|
|
693
|
-
this.session.model = p.defaultModel;
|
|
695
|
+
this.session.model = p.defaultModel || p.models[0] || '';
|
|
694
696
|
}
|
|
695
697
|
if (!this.session.providerName) {
|
|
696
698
|
this.session.providerName = p.name;
|
|
697
|
-
this.session.model = p.defaultModel;
|
|
699
|
+
this.session.model = p.defaultModel || p.models[0] || '';
|
|
698
700
|
}
|
|
699
701
|
this.emit('update');
|
|
700
702
|
}
|
|
@@ -731,7 +733,7 @@ export class Controller extends EventEmitter {
|
|
|
731
733
|
if (this.session.providerName.toLowerCase() === name.toLowerCase()) {
|
|
732
734
|
const fallback = this.config.providers[0];
|
|
733
735
|
this.session.providerName = fallback?.name ?? '';
|
|
734
|
-
this.session.model = fallback?.defaultModel
|
|
736
|
+
this.session.model = fallback?.defaultModel || fallback?.models[0] || '';
|
|
735
737
|
}
|
|
736
738
|
saveConfig(this.config);
|
|
737
739
|
this.llmCache.clear();
|