hedgequantx 2.7.19 → 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/pages/ai-agents.js +105 -71
- package/src/pages/ai-models.js +84 -0
package/package.json
CHANGED
package/src/pages/ai-agents.js
CHANGED
|
@@ -12,6 +12,7 @@ const fs = require('fs');
|
|
|
12
12
|
|
|
13
13
|
const { getLogoWidth, centerText, visibleLength } = require('../ui');
|
|
14
14
|
const { prompts } = require('../utils');
|
|
15
|
+
const { getModelsForProvider, getModelById } = require('./ai-models');
|
|
15
16
|
|
|
16
17
|
// Config file path
|
|
17
18
|
const CONFIG_DIR = path.join(os.homedir(), '.hqx');
|
|
@@ -63,7 +64,7 @@ const saveConfig = (config) => {
|
|
|
63
64
|
};
|
|
64
65
|
|
|
65
66
|
/**
|
|
66
|
-
* Mask API key for display
|
|
67
|
+
* Mask API key for display
|
|
67
68
|
* @param {string} key - API key
|
|
68
69
|
* @returns {string} Masked key
|
|
69
70
|
*/
|
|
@@ -73,71 +74,98 @@ const maskKey = (key) => {
|
|
|
73
74
|
};
|
|
74
75
|
|
|
75
76
|
/**
|
|
76
|
-
* Draw
|
|
77
|
-
* @param {Object} config - Current config
|
|
78
|
-
* @param {number} boxWidth - Box width
|
|
77
|
+
* Draw a 2-column row
|
|
79
78
|
*/
|
|
80
|
-
const
|
|
81
|
-
const W = boxWidth - 2;
|
|
79
|
+
const draw2ColRow = (leftText, rightText, W) => {
|
|
82
80
|
const col1Width = Math.floor(W / 2);
|
|
83
81
|
const col2Width = W - col1Width;
|
|
84
|
-
|
|
85
|
-
|
|
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) => {
|
|
86
100
|
console.log(chalk.cyan('╔' + '═'.repeat(W) + '╗'));
|
|
87
|
-
console.log(chalk.cyan('║') +
|
|
101
|
+
console.log(chalk.cyan('║') + titleColor(centerText(title, W)) + chalk.cyan('║'));
|
|
88
102
|
console.log(chalk.cyan('╠' + '═'.repeat(W) + '╣'));
|
|
89
103
|
|
|
90
|
-
|
|
91
|
-
const maxNameLen = Math.max(...AI_PROVIDERS.map(p => p.name.length));
|
|
92
|
-
|
|
93
|
-
// Provider rows (2 columns)
|
|
94
|
-
const rows = Math.ceil(AI_PROVIDERS.length / 2);
|
|
104
|
+
const rows = Math.ceil(items.length / 2);
|
|
95
105
|
for (let row = 0; row < rows; row++) {
|
|
96
|
-
const
|
|
97
|
-
const
|
|
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);
|
|
98
153
|
|
|
99
|
-
const
|
|
100
|
-
const
|
|
154
|
+
const input = await prompts.textInput(chalk.cyan('Select model: '));
|
|
155
|
+
const choice = (input || '').toLowerCase().trim();
|
|
101
156
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const leftConfig = config.providers[leftProvider.id] || {};
|
|
106
|
-
const leftStatus = leftConfig.active ? chalk.green('●') : '';
|
|
107
|
-
const leftText = chalk.cyan(leftNum) + ' ' + chalk[leftProvider.color](leftName) + ' ' + leftStatus;
|
|
108
|
-
const leftLen = visibleLength(leftText);
|
|
109
|
-
const leftPadTotal = col1Width - leftLen;
|
|
110
|
-
const leftPadL = Math.floor(leftPadTotal / 2);
|
|
111
|
-
const leftPadR = leftPadTotal - leftPadL;
|
|
157
|
+
if (choice === 'b' || choice === '') {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
112
160
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
let rightPadR = col2Width;
|
|
117
|
-
if (rightProvider) {
|
|
118
|
-
const rightNum = `[${rightIdx + 1}]`;
|
|
119
|
-
const rightName = rightProvider.name;
|
|
120
|
-
const rightConfig = config.providers[rightProvider.id] || {};
|
|
121
|
-
const rightStatus = rightConfig.active ? chalk.green('●') : '';
|
|
122
|
-
rightText = chalk.cyan(rightNum) + ' ' + chalk[rightProvider.color](rightName) + ' ' + rightStatus;
|
|
123
|
-
const rightLen = visibleLength(rightText);
|
|
124
|
-
const rightPadTotal = col2Width - rightLen;
|
|
125
|
-
rightPadL = Math.floor(rightPadTotal / 2);
|
|
126
|
-
rightPadR = rightPadTotal - rightPadL;
|
|
161
|
+
const num = parseInt(choice);
|
|
162
|
+
if (!isNaN(num) && num >= 1 && num <= models.length) {
|
|
163
|
+
return models[num - 1];
|
|
127
164
|
}
|
|
128
165
|
|
|
129
|
-
console.log(
|
|
130
|
-
|
|
131
|
-
' '.repeat(leftPadL) + leftText + ' '.repeat(leftPadR) +
|
|
132
|
-
' '.repeat(rightPadL) + rightText + ' '.repeat(rightPadR) +
|
|
133
|
-
chalk.cyan('║')
|
|
134
|
-
);
|
|
166
|
+
console.log(chalk.red(' Invalid option.'));
|
|
167
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
135
168
|
}
|
|
136
|
-
|
|
137
|
-
// Footer
|
|
138
|
-
console.log(chalk.cyan('╠' + '─'.repeat(W) + '╣'));
|
|
139
|
-
console.log(chalk.cyan('║') + chalk.red(centerText('[B] Back to Menu', W)) + chalk.cyan('║'));
|
|
140
|
-
console.log(chalk.cyan('╚' + '═'.repeat(W) + '╝'));
|
|
141
169
|
};
|
|
142
170
|
|
|
143
171
|
/**
|
|
@@ -213,11 +241,8 @@ const drawProviderWindow = (provider, config, boxWidth) => {
|
|
|
213
241
|
let statusText = '';
|
|
214
242
|
if (providerConfig.active) {
|
|
215
243
|
const connType = providerConfig.connectionType === 'cliproxy' ? 'CLIProxy' : 'API Key';
|
|
216
|
-
const
|
|
217
|
-
statusText = chalk.green('● ACTIVE') + chalk.gray(' via ') + chalk.cyan(connType);
|
|
218
|
-
if (providerConfig.connectionType === 'apikey' && providerConfig.apiKey) {
|
|
219
|
-
statusText += chalk.gray(' Key: ') + chalk.cyan(keyDisplay);
|
|
220
|
-
}
|
|
244
|
+
const modelName = providerConfig.modelName || 'N/A';
|
|
245
|
+
statusText = chalk.green('● ACTIVE') + chalk.gray(' Model: ') + chalk.yellow(modelName) + chalk.gray(' via ') + chalk.cyan(connType);
|
|
221
246
|
} else if (providerConfig.apiKey || providerConfig.connectionType) {
|
|
222
247
|
statusText = chalk.yellow('● CONFIGURED') + chalk.gray(' (not active)');
|
|
223
248
|
} else {
|
|
@@ -269,11 +294,9 @@ const handleProviderConfig = async (provider, config) => {
|
|
|
269
294
|
}
|
|
270
295
|
|
|
271
296
|
if (choice === '1') {
|
|
272
|
-
// CLIProxy connection
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
console.log(chalk.gray(' This uses your paid plan (Claude Pro, ChatGPT Plus, etc.)'));
|
|
276
|
-
console.log();
|
|
297
|
+
// CLIProxy connection - select model first
|
|
298
|
+
const selectedModel = await selectModel(provider);
|
|
299
|
+
if (!selectedModel) continue;
|
|
277
300
|
|
|
278
301
|
// Deactivate all other providers
|
|
279
302
|
Object.keys(config.providers).forEach(id => {
|
|
@@ -282,22 +305,28 @@ const handleProviderConfig = async (provider, config) => {
|
|
|
282
305
|
|
|
283
306
|
if (!config.providers[provider.id]) config.providers[provider.id] = {};
|
|
284
307
|
config.providers[provider.id].connectionType = 'cliproxy';
|
|
308
|
+
config.providers[provider.id].modelId = selectedModel.id;
|
|
309
|
+
config.providers[provider.id].modelName = selectedModel.name;
|
|
285
310
|
config.providers[provider.id].active = true;
|
|
286
311
|
config.providers[provider.id].configuredAt = new Date().toISOString();
|
|
287
312
|
|
|
288
313
|
if (saveConfig(config)) {
|
|
289
|
-
console.log(chalk.green(
|
|
314
|
+
console.log(chalk.green(`\n ✓ ${provider.name} connected via CLIProxy.`));
|
|
315
|
+
console.log(chalk.cyan(` Model: ${selectedModel.name}`));
|
|
290
316
|
} else {
|
|
291
|
-
console.log(chalk.red(' Failed to save config.'));
|
|
317
|
+
console.log(chalk.red('\n Failed to save config.'));
|
|
292
318
|
}
|
|
293
319
|
await prompts.waitForEnter();
|
|
294
320
|
continue;
|
|
295
321
|
}
|
|
296
322
|
|
|
297
323
|
if (choice === '2') {
|
|
298
|
-
// API Key connection
|
|
299
|
-
|
|
300
|
-
|
|
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:`));
|
|
301
330
|
console.log(chalk.gray(' (Press Enter to cancel)'));
|
|
302
331
|
console.log();
|
|
303
332
|
|
|
@@ -323,13 +352,16 @@ const handleProviderConfig = async (provider, config) => {
|
|
|
323
352
|
if (!config.providers[provider.id]) config.providers[provider.id] = {};
|
|
324
353
|
config.providers[provider.id].connectionType = 'apikey';
|
|
325
354
|
config.providers[provider.id].apiKey = apiKey.trim();
|
|
355
|
+
config.providers[provider.id].modelId = selectedModel.id;
|
|
356
|
+
config.providers[provider.id].modelName = selectedModel.name;
|
|
326
357
|
config.providers[provider.id].active = true;
|
|
327
358
|
config.providers[provider.id].configuredAt = new Date().toISOString();
|
|
328
359
|
|
|
329
360
|
if (saveConfig(config)) {
|
|
330
|
-
console.log(chalk.green(
|
|
361
|
+
console.log(chalk.green(`\n ✓ ${provider.name} connected via API Key.`));
|
|
362
|
+
console.log(chalk.cyan(` Model: ${selectedModel.name}`));
|
|
331
363
|
} else {
|
|
332
|
-
console.log(chalk.red(' Failed to save config.'));
|
|
364
|
+
console.log(chalk.red('\n Failed to save config.'));
|
|
333
365
|
}
|
|
334
366
|
await prompts.waitForEnter();
|
|
335
367
|
continue;
|
|
@@ -352,7 +384,9 @@ const getActiveProvider = () => {
|
|
|
352
384
|
id: provider.id,
|
|
353
385
|
name: provider.name,
|
|
354
386
|
connectionType: providerConfig.connectionType,
|
|
355
|
-
apiKey: providerConfig.apiKey || null
|
|
387
|
+
apiKey: providerConfig.apiKey || null,
|
|
388
|
+
modelId: providerConfig.modelId || null,
|
|
389
|
+
modelName: providerConfig.modelName || null
|
|
356
390
|
};
|
|
357
391
|
}
|
|
358
392
|
}
|
|
@@ -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
|
+
};
|