hedgequantx 2.7.18 → 2.7.20
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/package.json +1 -1
- package/src/app.js +2 -3
- package/src/pages/ai-agents.js +441 -0
- package/src/pages/ai-models.js +84 -0
package/package.json
CHANGED
package/src/app.js
CHANGED
|
@@ -17,6 +17,7 @@ const log = logger.scope('App');
|
|
|
17
17
|
const { showStats } = require('./pages/stats');
|
|
18
18
|
const { showAccounts } = require('./pages/accounts');
|
|
19
19
|
const { algoTradingMenu } = require('./pages/algo');
|
|
20
|
+
const { aiAgentsMenu, getActiveAgentCount } = require('./pages/ai-agents');
|
|
20
21
|
|
|
21
22
|
// Menus
|
|
22
23
|
const { rithmicMenu, dashboardMenu, handleUpdate } = require('./menus');
|
|
@@ -296,9 +297,7 @@ const run = async () => {
|
|
|
296
297
|
break;
|
|
297
298
|
|
|
298
299
|
case 'aiagents':
|
|
299
|
-
|
|
300
|
-
console.log(chalk.gray(' Configure AI trading agents for automated strategies.'));
|
|
301
|
-
await prompts.waitForEnter();
|
|
300
|
+
await aiAgentsMenu();
|
|
302
301
|
break;
|
|
303
302
|
|
|
304
303
|
case 'update':
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Agents Configuration Page
|
|
3
|
+
*
|
|
4
|
+
* Allows users to configure AI providers for trading strategies.
|
|
5
|
+
* Supports both CLIProxy (paid plans) and direct API keys.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
|
|
13
|
+
const { getLogoWidth, centerText, visibleLength } = require('../ui');
|
|
14
|
+
const { prompts } = require('../utils');
|
|
15
|
+
const { getModelsForProvider, getModelById } = require('./ai-models');
|
|
16
|
+
|
|
17
|
+
// Config file path
|
|
18
|
+
const CONFIG_DIR = path.join(os.homedir(), '.hqx');
|
|
19
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'ai-config.json');
|
|
20
|
+
|
|
21
|
+
// AI Providers list
|
|
22
|
+
const AI_PROVIDERS = [
|
|
23
|
+
{ id: 'anthropic', name: 'Anthropic (Claude)', color: 'magenta' },
|
|
24
|
+
{ id: 'openai', name: 'OpenAI (GPT)', color: 'green' },
|
|
25
|
+
{ id: 'google', name: 'Google (Gemini)', color: 'blue' },
|
|
26
|
+
{ id: 'mistral', name: 'Mistral AI', color: 'yellow' },
|
|
27
|
+
{ id: 'groq', name: 'Groq', color: 'cyan' },
|
|
28
|
+
{ id: 'xai', name: 'xAI (Grok)', color: 'white' },
|
|
29
|
+
{ id: 'perplexity', name: 'Perplexity', color: 'blue' },
|
|
30
|
+
{ id: 'openrouter', name: 'OpenRouter', color: 'gray' },
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Load AI config from file
|
|
35
|
+
* @returns {Object} Config object with provider settings
|
|
36
|
+
*/
|
|
37
|
+
const loadConfig = () => {
|
|
38
|
+
try {
|
|
39
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
40
|
+
const data = fs.readFileSync(CONFIG_FILE, 'utf8');
|
|
41
|
+
return JSON.parse(data);
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
// Config file doesn't exist or is invalid
|
|
45
|
+
}
|
|
46
|
+
return { providers: {} };
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Save AI config to file
|
|
51
|
+
* @param {Object} config - Config object to save
|
|
52
|
+
* @returns {boolean} Success status
|
|
53
|
+
*/
|
|
54
|
+
const saveConfig = (config) => {
|
|
55
|
+
try {
|
|
56
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
57
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
58
|
+
}
|
|
59
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
60
|
+
return true;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Mask API key for display
|
|
68
|
+
* @param {string} key - API key
|
|
69
|
+
* @returns {string} Masked key
|
|
70
|
+
*/
|
|
71
|
+
const maskKey = (key) => {
|
|
72
|
+
if (!key || key.length < 16) return '****';
|
|
73
|
+
return key.substring(0, 8) + '...' + key.substring(key.length - 4);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Draw a 2-column row
|
|
78
|
+
*/
|
|
79
|
+
const draw2ColRow = (leftText, rightText, W) => {
|
|
80
|
+
const col1Width = Math.floor(W / 2);
|
|
81
|
+
const col2Width = W - col1Width;
|
|
82
|
+
const leftLen = visibleLength(leftText);
|
|
83
|
+
const leftPad = col1Width - leftLen;
|
|
84
|
+
const leftPadL = Math.floor(leftPad / 2);
|
|
85
|
+
const rightLen = visibleLength(rightText || '');
|
|
86
|
+
const rightPad = col2Width - rightLen;
|
|
87
|
+
const rightPadL = Math.floor(rightPad / 2);
|
|
88
|
+
console.log(
|
|
89
|
+
chalk.cyan('║') +
|
|
90
|
+
' '.repeat(leftPadL) + leftText + ' '.repeat(leftPad - leftPadL) +
|
|
91
|
+
' '.repeat(rightPadL) + (rightText || '') + ' '.repeat(rightPad - rightPadL) +
|
|
92
|
+
chalk.cyan('║')
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Draw 2-column table
|
|
98
|
+
*/
|
|
99
|
+
const draw2ColTable = (title, titleColor, items, backText, W) => {
|
|
100
|
+
console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
|
|
101
|
+
console.log(chalk.cyan('║') + titleColor(centerText(title, W)) + chalk.cyan('║'));
|
|
102
|
+
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
103
|
+
|
|
104
|
+
const rows = Math.ceil(items.length / 2);
|
|
105
|
+
for (let row = 0; row < rows; row++) {
|
|
106
|
+
const left = items[row];
|
|
107
|
+
const right = items[row + rows];
|
|
108
|
+
draw2ColRow(left || '', right || '', W);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
|
|
112
|
+
console.log(chalk.cyan('║') + chalk.red(centerText(backText, W)) + chalk.cyan('║'));
|
|
113
|
+
console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Draw providers table
|
|
118
|
+
*/
|
|
119
|
+
const drawProvidersTable = (config, boxWidth) => {
|
|
120
|
+
const W = boxWidth - 2;
|
|
121
|
+
const items = AI_PROVIDERS.map((p, i) => {
|
|
122
|
+
const status = config.providers[p.id]?.active ? chalk.green(' ●') : '';
|
|
123
|
+
return chalk.cyan(`[${i + 1}]`) + ' ' + chalk[p.color](p.name) + status;
|
|
124
|
+
});
|
|
125
|
+
draw2ColTable('AI AGENTS CONFIGURATION', chalk.yellow.bold, items, '[B] Back to Menu', W);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Draw models table
|
|
130
|
+
*/
|
|
131
|
+
const drawModelsTable = (provider, models, boxWidth) => {
|
|
132
|
+
const W = boxWidth - 2;
|
|
133
|
+
const items = models.map((m, i) => chalk.cyan(`[${i + 1}]`) + ' ' + chalk.white(m.name));
|
|
134
|
+
draw2ColTable(`${provider.name.toUpperCase()} - MODELS`, chalk[provider.color].bold, items, '[B] Back', W);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Select a model for a provider
|
|
139
|
+
* @param {Object} provider - Provider object
|
|
140
|
+
* @returns {Object|null} Selected model or null if cancelled
|
|
141
|
+
*/
|
|
142
|
+
const selectModel = async (provider) => {
|
|
143
|
+
const boxWidth = getLogoWidth();
|
|
144
|
+
const models = getModelsForProvider(provider.id);
|
|
145
|
+
|
|
146
|
+
if (models.length === 0) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
while (true) {
|
|
151
|
+
console.clear();
|
|
152
|
+
drawModelsTable(provider, models, boxWidth);
|
|
153
|
+
|
|
154
|
+
const input = await prompts.textInput(chalk.cyan('Select model: '));
|
|
155
|
+
const choice = (input || '').toLowerCase().trim();
|
|
156
|
+
|
|
157
|
+
if (choice === 'b' || choice === '') {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const num = parseInt(choice);
|
|
162
|
+
if (!isNaN(num) && num >= 1 && num <= models.length) {
|
|
163
|
+
return models[num - 1];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
console.log(chalk.red(' Invalid option.'));
|
|
167
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Draw provider configuration window
|
|
173
|
+
* @param {Object} provider - Provider object
|
|
174
|
+
* @param {Object} config - Current config
|
|
175
|
+
* @param {number} boxWidth - Box width
|
|
176
|
+
*/
|
|
177
|
+
const drawProviderWindow = (provider, config, boxWidth) => {
|
|
178
|
+
const W = boxWidth - 2;
|
|
179
|
+
const col1Width = Math.floor(W / 2);
|
|
180
|
+
const col2Width = W - col1Width;
|
|
181
|
+
const providerConfig = config.providers[provider.id] || {};
|
|
182
|
+
|
|
183
|
+
// Header
|
|
184
|
+
console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
|
|
185
|
+
console.log(chalk.cyan('║') + chalk[provider.color].bold(centerText(provider.name.toUpperCase(), W)) + chalk.cyan('║'));
|
|
186
|
+
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
187
|
+
|
|
188
|
+
// Empty line
|
|
189
|
+
console.log(chalk.cyan('║') + ' '.repeat(W) + chalk.cyan('║'));
|
|
190
|
+
|
|
191
|
+
// Options in 2 columns
|
|
192
|
+
const opt1Title = '[1] Connect via Paid Plan';
|
|
193
|
+
const opt1Desc = 'Uses CLIProxy - No API key needed';
|
|
194
|
+
const opt2Title = '[2] Connect via API Key';
|
|
195
|
+
const opt2Desc = 'Enter your own API key';
|
|
196
|
+
|
|
197
|
+
// Row 1: Titles
|
|
198
|
+
const left1 = chalk.green(opt1Title);
|
|
199
|
+
const right1 = chalk.yellow(opt2Title);
|
|
200
|
+
const left1Len = visibleLength(left1);
|
|
201
|
+
const right1Len = visibleLength(right1);
|
|
202
|
+
const left1PadTotal = col1Width - left1Len;
|
|
203
|
+
const left1PadL = Math.floor(left1PadTotal / 2);
|
|
204
|
+
const left1PadR = left1PadTotal - left1PadL;
|
|
205
|
+
const right1PadTotal = col2Width - right1Len;
|
|
206
|
+
const right1PadL = Math.floor(right1PadTotal / 2);
|
|
207
|
+
const right1PadR = right1PadTotal - right1PadL;
|
|
208
|
+
|
|
209
|
+
console.log(
|
|
210
|
+
chalk.cyan('║') +
|
|
211
|
+
' '.repeat(left1PadL) + left1 + ' '.repeat(left1PadR) +
|
|
212
|
+
' '.repeat(right1PadL) + right1 + ' '.repeat(right1PadR) +
|
|
213
|
+
chalk.cyan('║')
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
// Row 2: Descriptions
|
|
217
|
+
const left2 = chalk.gray(opt1Desc);
|
|
218
|
+
const right2 = chalk.gray(opt2Desc);
|
|
219
|
+
const left2Len = visibleLength(left2);
|
|
220
|
+
const right2Len = visibleLength(right2);
|
|
221
|
+
const left2PadTotal = col1Width - left2Len;
|
|
222
|
+
const left2PadL = Math.floor(left2PadTotal / 2);
|
|
223
|
+
const left2PadR = left2PadTotal - left2PadL;
|
|
224
|
+
const right2PadTotal = col2Width - right2Len;
|
|
225
|
+
const right2PadL = Math.floor(right2PadTotal / 2);
|
|
226
|
+
const right2PadR = right2PadTotal - right2PadL;
|
|
227
|
+
|
|
228
|
+
console.log(
|
|
229
|
+
chalk.cyan('║') +
|
|
230
|
+
' '.repeat(left2PadL) + left2 + ' '.repeat(left2PadR) +
|
|
231
|
+
' '.repeat(right2PadL) + right2 + ' '.repeat(right2PadR) +
|
|
232
|
+
chalk.cyan('║')
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
// Empty line
|
|
236
|
+
console.log(chalk.cyan('║') + ' '.repeat(W) + chalk.cyan('║'));
|
|
237
|
+
|
|
238
|
+
// Status bar
|
|
239
|
+
console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
|
|
240
|
+
|
|
241
|
+
let statusText = '';
|
|
242
|
+
if (providerConfig.active) {
|
|
243
|
+
const connType = providerConfig.connectionType === 'cliproxy' ? 'CLIProxy' : 'API Key';
|
|
244
|
+
const modelName = providerConfig.modelName || 'N/A';
|
|
245
|
+
statusText = chalk.green('● ACTIVE') + chalk.gray(' Model: ') + chalk.yellow(modelName) + chalk.gray(' via ') + chalk.cyan(connType);
|
|
246
|
+
} else if (providerConfig.apiKey || providerConfig.connectionType) {
|
|
247
|
+
statusText = chalk.yellow('● CONFIGURED') + chalk.gray(' (not active)');
|
|
248
|
+
} else {
|
|
249
|
+
statusText = chalk.gray('○ NOT CONFIGURED');
|
|
250
|
+
}
|
|
251
|
+
console.log(chalk.cyan('║') + centerText(statusText, W) + chalk.cyan('║'));
|
|
252
|
+
|
|
253
|
+
// Disconnect option if active
|
|
254
|
+
if (providerConfig.active) {
|
|
255
|
+
console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
|
|
256
|
+
console.log(chalk.cyan('║') + chalk.red(centerText('[D] Disconnect', W)) + chalk.cyan('║'));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Back
|
|
260
|
+
console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
|
|
261
|
+
console.log(chalk.cyan('║') + chalk.red(centerText('[B] Back', W)) + chalk.cyan('║'));
|
|
262
|
+
console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Handle provider configuration
|
|
267
|
+
* @param {Object} provider - Provider to configure
|
|
268
|
+
* @param {Object} config - Current config
|
|
269
|
+
* @returns {Object} Updated config
|
|
270
|
+
*/
|
|
271
|
+
const handleProviderConfig = async (provider, config) => {
|
|
272
|
+
const boxWidth = getLogoWidth();
|
|
273
|
+
|
|
274
|
+
while (true) {
|
|
275
|
+
console.clear();
|
|
276
|
+
drawProviderWindow(provider, config, boxWidth);
|
|
277
|
+
|
|
278
|
+
const input = await prompts.textInput(chalk.cyan('Select option: '));
|
|
279
|
+
const choice = (input || '').toLowerCase().trim();
|
|
280
|
+
|
|
281
|
+
if (choice === 'b' || choice === '') {
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (choice === 'd') {
|
|
286
|
+
// Disconnect
|
|
287
|
+
if (config.providers[provider.id]) {
|
|
288
|
+
config.providers[provider.id].active = false;
|
|
289
|
+
saveConfig(config);
|
|
290
|
+
console.log(chalk.yellow(`\n ${provider.name} disconnected.`));
|
|
291
|
+
await prompts.waitForEnter();
|
|
292
|
+
}
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (choice === '1') {
|
|
297
|
+
// CLIProxy connection - select model first
|
|
298
|
+
const selectedModel = await selectModel(provider);
|
|
299
|
+
if (!selectedModel) continue;
|
|
300
|
+
|
|
301
|
+
// Deactivate all other providers
|
|
302
|
+
Object.keys(config.providers).forEach(id => {
|
|
303
|
+
if (config.providers[id]) config.providers[id].active = false;
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
if (!config.providers[provider.id]) config.providers[provider.id] = {};
|
|
307
|
+
config.providers[provider.id].connectionType = 'cliproxy';
|
|
308
|
+
config.providers[provider.id].modelId = selectedModel.id;
|
|
309
|
+
config.providers[provider.id].modelName = selectedModel.name;
|
|
310
|
+
config.providers[provider.id].active = true;
|
|
311
|
+
config.providers[provider.id].configuredAt = new Date().toISOString();
|
|
312
|
+
|
|
313
|
+
if (saveConfig(config)) {
|
|
314
|
+
console.log(chalk.green(`\n ✓ ${provider.name} connected via CLIProxy.`));
|
|
315
|
+
console.log(chalk.cyan(` Model: ${selectedModel.name}`));
|
|
316
|
+
} else {
|
|
317
|
+
console.log(chalk.red('\n Failed to save config.'));
|
|
318
|
+
}
|
|
319
|
+
await prompts.waitForEnter();
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (choice === '2') {
|
|
324
|
+
// API Key connection - select model first
|
|
325
|
+
const selectedModel = await selectModel(provider);
|
|
326
|
+
if (!selectedModel) continue;
|
|
327
|
+
|
|
328
|
+
console.clear();
|
|
329
|
+
console.log(chalk.yellow(`\n Enter your ${provider.name} API key:`));
|
|
330
|
+
console.log(chalk.gray(' (Press Enter to cancel)'));
|
|
331
|
+
console.log();
|
|
332
|
+
|
|
333
|
+
const apiKey = await prompts.textInput(chalk.cyan(' API Key: '), true);
|
|
334
|
+
|
|
335
|
+
if (!apiKey || apiKey.trim() === '') {
|
|
336
|
+
console.log(chalk.gray(' Cancelled.'));
|
|
337
|
+
await prompts.waitForEnter();
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (apiKey.length < 20) {
|
|
342
|
+
console.log(chalk.red(' Invalid API key format (too short).'));
|
|
343
|
+
await prompts.waitForEnter();
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Deactivate all other providers
|
|
348
|
+
Object.keys(config.providers).forEach(id => {
|
|
349
|
+
if (config.providers[id]) config.providers[id].active = false;
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
if (!config.providers[provider.id]) config.providers[provider.id] = {};
|
|
353
|
+
config.providers[provider.id].connectionType = 'apikey';
|
|
354
|
+
config.providers[provider.id].apiKey = apiKey.trim();
|
|
355
|
+
config.providers[provider.id].modelId = selectedModel.id;
|
|
356
|
+
config.providers[provider.id].modelName = selectedModel.name;
|
|
357
|
+
config.providers[provider.id].active = true;
|
|
358
|
+
config.providers[provider.id].configuredAt = new Date().toISOString();
|
|
359
|
+
|
|
360
|
+
if (saveConfig(config)) {
|
|
361
|
+
console.log(chalk.green(`\n ✓ ${provider.name} connected via API Key.`));
|
|
362
|
+
console.log(chalk.cyan(` Model: ${selectedModel.name}`));
|
|
363
|
+
} else {
|
|
364
|
+
console.log(chalk.red('\n Failed to save config.'));
|
|
365
|
+
}
|
|
366
|
+
await prompts.waitForEnter();
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return config;
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Get active AI provider config
|
|
376
|
+
* @returns {Object|null} Active provider config or null
|
|
377
|
+
*/
|
|
378
|
+
const getActiveProvider = () => {
|
|
379
|
+
const config = loadConfig();
|
|
380
|
+
for (const provider of AI_PROVIDERS) {
|
|
381
|
+
const providerConfig = config.providers[provider.id];
|
|
382
|
+
if (providerConfig && providerConfig.active) {
|
|
383
|
+
return {
|
|
384
|
+
id: provider.id,
|
|
385
|
+
name: provider.name,
|
|
386
|
+
connectionType: providerConfig.connectionType,
|
|
387
|
+
apiKey: providerConfig.apiKey || null,
|
|
388
|
+
modelId: providerConfig.modelId || null,
|
|
389
|
+
modelName: providerConfig.modelName || null
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return null;
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Count active AI agents
|
|
398
|
+
* @returns {number} Number of active agents (0 or 1)
|
|
399
|
+
*/
|
|
400
|
+
const getActiveAgentCount = () => {
|
|
401
|
+
const active = getActiveProvider();
|
|
402
|
+
return active ? 1 : 0;
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Main AI Agents menu
|
|
407
|
+
*/
|
|
408
|
+
const aiAgentsMenu = async () => {
|
|
409
|
+
let config = loadConfig();
|
|
410
|
+
const boxWidth = getLogoWidth();
|
|
411
|
+
|
|
412
|
+
while (true) {
|
|
413
|
+
console.clear();
|
|
414
|
+
drawProvidersTable(config, boxWidth);
|
|
415
|
+
|
|
416
|
+
const input = await prompts.textInput(chalk.cyan('Select provider: '));
|
|
417
|
+
const choice = (input || '').toLowerCase().trim();
|
|
418
|
+
|
|
419
|
+
if (choice === 'b' || choice === '') {
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const num = parseInt(choice);
|
|
424
|
+
if (!isNaN(num) && num >= 1 && num <= AI_PROVIDERS.length) {
|
|
425
|
+
config = await handleProviderConfig(AI_PROVIDERS[num - 1], config);
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
console.log(chalk.red(' Invalid option.'));
|
|
430
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
module.exports = {
|
|
435
|
+
aiAgentsMenu,
|
|
436
|
+
getActiveProvider,
|
|
437
|
+
getActiveAgentCount,
|
|
438
|
+
loadConfig,
|
|
439
|
+
saveConfig,
|
|
440
|
+
AI_PROVIDERS
|
|
441
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Models Configuration
|
|
3
|
+
*
|
|
4
|
+
* Lists available models for each AI provider.
|
|
5
|
+
* These are technical configuration values, not trading data.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Models by provider ID
|
|
9
|
+
const PROVIDER_MODELS = {
|
|
10
|
+
anthropic: [
|
|
11
|
+
{ id: 'claude-opus-4-20250514', name: 'Claude Opus 4', tier: 'flagship' },
|
|
12
|
+
{ id: 'claude-sonnet-4-20250514', name: 'Claude Sonnet 4', tier: 'balanced' },
|
|
13
|
+
{ id: 'claude-3-5-sonnet-20241022', name: 'Claude Sonnet 3.5', tier: 'balanced' },
|
|
14
|
+
{ id: 'claude-3-5-haiku-20241022', name: 'Claude Haiku 3.5', tier: 'fast' },
|
|
15
|
+
],
|
|
16
|
+
openai: [
|
|
17
|
+
{ id: 'gpt-4o', name: 'GPT-4o', tier: 'flagship' },
|
|
18
|
+
{ id: 'gpt-4o-mini', name: 'GPT-4o Mini', tier: 'fast' },
|
|
19
|
+
{ id: 'gpt-4-turbo', name: 'GPT-4 Turbo', tier: 'balanced' },
|
|
20
|
+
{ id: 'o1', name: 'o1', tier: 'reasoning' },
|
|
21
|
+
{ id: 'o1-mini', name: 'o1-mini', tier: 'reasoning' },
|
|
22
|
+
{ id: 'o3-mini', name: 'o3-mini', tier: 'reasoning' },
|
|
23
|
+
],
|
|
24
|
+
google: [
|
|
25
|
+
{ id: 'gemini-2.0-flash', name: 'Gemini 2.0 Flash', tier: 'flagship' },
|
|
26
|
+
{ id: 'gemini-1.5-pro', name: 'Gemini 1.5 Pro', tier: 'balanced' },
|
|
27
|
+
{ id: 'gemini-1.5-flash', name: 'Gemini 1.5 Flash', tier: 'fast' },
|
|
28
|
+
{ id: 'gemini-1.0-pro', name: 'Gemini 1.0 Pro', tier: 'legacy' },
|
|
29
|
+
],
|
|
30
|
+
mistral: [
|
|
31
|
+
{ id: 'mistral-large-latest', name: 'Mistral Large', tier: 'flagship' },
|
|
32
|
+
{ id: 'mistral-medium-latest', name: 'Mistral Medium', tier: 'balanced' },
|
|
33
|
+
{ id: 'mistral-small-latest', name: 'Mistral Small', tier: 'fast' },
|
|
34
|
+
{ id: 'codestral-latest', name: 'Codestral', tier: 'code' },
|
|
35
|
+
],
|
|
36
|
+
groq: [
|
|
37
|
+
{ id: 'llama-3.3-70b-versatile', name: 'Llama 3.3 70B', tier: 'flagship' },
|
|
38
|
+
{ id: 'llama-3.1-8b-instant', name: 'Llama 3.1 8B', tier: 'fast' },
|
|
39
|
+
{ id: 'mixtral-8x7b-32768', name: 'Mixtral 8x7B', tier: 'balanced' },
|
|
40
|
+
{ id: 'gemma2-9b-it', name: 'Gemma 2 9B', tier: 'fast' },
|
|
41
|
+
],
|
|
42
|
+
xai: [
|
|
43
|
+
{ id: 'grok-2', name: 'Grok 2', tier: 'flagship' },
|
|
44
|
+
{ id: 'grok-2-mini', name: 'Grok 2 Mini', tier: 'fast' },
|
|
45
|
+
{ id: 'grok-beta', name: 'Grok Beta', tier: 'beta' },
|
|
46
|
+
],
|
|
47
|
+
perplexity: [
|
|
48
|
+
{ id: 'sonar-pro', name: 'Sonar Pro', tier: 'flagship' },
|
|
49
|
+
{ id: 'sonar', name: 'Sonar', tier: 'balanced' },
|
|
50
|
+
{ id: 'sonar-reasoning', name: 'Sonar Reasoning', tier: 'reasoning' },
|
|
51
|
+
],
|
|
52
|
+
openrouter: [
|
|
53
|
+
{ id: 'anthropic/claude-opus-4', name: 'Claude Opus 4', tier: 'flagship' },
|
|
54
|
+
{ id: 'openai/gpt-4o', name: 'GPT-4o', tier: 'flagship' },
|
|
55
|
+
{ id: 'google/gemini-2.0-flash', name: 'Gemini 2.0 Flash', tier: 'flagship' },
|
|
56
|
+
{ id: 'meta-llama/llama-3.3-70b', name: 'Llama 3.3 70B', tier: 'open' },
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get models for a provider
|
|
62
|
+
* @param {string} providerId - Provider ID
|
|
63
|
+
* @returns {Array} List of models
|
|
64
|
+
*/
|
|
65
|
+
const getModelsForProvider = (providerId) => {
|
|
66
|
+
return PROVIDER_MODELS[providerId] || [];
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get model by ID
|
|
71
|
+
* @param {string} providerId - Provider ID
|
|
72
|
+
* @param {string} modelId - Model ID
|
|
73
|
+
* @returns {Object|null} Model object or null
|
|
74
|
+
*/
|
|
75
|
+
const getModelById = (providerId, modelId) => {
|
|
76
|
+
const models = PROVIDER_MODELS[providerId] || [];
|
|
77
|
+
return models.find(m => m.id === modelId) || null;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
module.exports = {
|
|
81
|
+
PROVIDER_MODELS,
|
|
82
|
+
getModelsForProvider,
|
|
83
|
+
getModelById
|
|
84
|
+
};
|