hedgequantx 2.5.22 → 2.5.23
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/menus/ai-agent.js +117 -31
- package/src/services/ai/client.js +63 -1
- package/src/services/ai/index.js +15 -0
- package/src/services/ai/providers/index.js +2 -15
package/package.json
CHANGED
package/src/menus/ai-agent.js
CHANGED
|
@@ -494,14 +494,39 @@ const showExistingTokens = async () => {
|
|
|
494
494
|
return await showExistingTokens();
|
|
495
495
|
}
|
|
496
496
|
|
|
497
|
-
|
|
498
|
-
|
|
497
|
+
spinner.text = 'FETCHING AVAILABLE MODELS...';
|
|
498
|
+
|
|
499
|
+
// Fetch models from API with the token
|
|
500
|
+
const { fetchAnthropicModels, fetchOpenAIModels } = require('../services/ai/client');
|
|
501
|
+
|
|
502
|
+
let models = null;
|
|
503
|
+
if (selectedToken.provider === 'anthropic') {
|
|
504
|
+
models = await fetchAnthropicModels(credentials.apiKey);
|
|
505
|
+
} else {
|
|
506
|
+
models = await fetchOpenAIModels(provider.endpoint, credentials.apiKey);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (!models || models.length === 0) {
|
|
510
|
+
spinner.fail('COULD NOT FETCH MODELS FROM API');
|
|
511
|
+
await prompts.waitForEnter();
|
|
512
|
+
return await showExistingTokens();
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
spinner.succeed(`FOUND ${models.length} MODELS`);
|
|
516
|
+
|
|
517
|
+
// Let user select model
|
|
518
|
+
const selectedModel = await selectModelFromList(models, provider.name);
|
|
519
|
+
if (!selectedModel) {
|
|
520
|
+
return await showExistingTokens();
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Add agent with selected model
|
|
499
524
|
const agentName = `${provider.name} (${selectedToken.source})`;
|
|
500
|
-
await aiService.addAgent(selectedToken.provider, 'api_key', credentials,
|
|
525
|
+
await aiService.addAgent(selectedToken.provider, 'api_key', credentials, selectedModel, agentName);
|
|
501
526
|
|
|
502
|
-
|
|
527
|
+
console.log(chalk.green(`\n AGENT ADDED: ${provider.name}`));
|
|
503
528
|
console.log(chalk.gray(` SOURCE: ${selectedToken.source}`));
|
|
504
|
-
console.log(chalk.gray(` MODEL: ${
|
|
529
|
+
console.log(chalk.gray(` MODEL: ${selectedModel}`));
|
|
505
530
|
|
|
506
531
|
await prompts.waitForEnter();
|
|
507
532
|
return await aiAgentMenu();
|
|
@@ -942,8 +967,67 @@ const setupConnection = async (provider, option) => {
|
|
|
942
967
|
return await aiAgentMenu();
|
|
943
968
|
};
|
|
944
969
|
|
|
970
|
+
/**
|
|
971
|
+
* Select model from a list (used when adding new agent)
|
|
972
|
+
* @param {Array} models - Array of model IDs from API
|
|
973
|
+
* @param {string} providerName - Provider name for display
|
|
974
|
+
* @returns {string|null} Selected model ID or null if cancelled
|
|
975
|
+
*
|
|
976
|
+
* Data source: models array comes from provider API (/v1/models)
|
|
977
|
+
*/
|
|
978
|
+
const selectModelFromList = async (models, providerName) => {
|
|
979
|
+
const boxWidth = getLogoWidth();
|
|
980
|
+
const W = boxWidth - 2;
|
|
981
|
+
|
|
982
|
+
const makeLine = (content) => {
|
|
983
|
+
const plainLen = content.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
984
|
+
const padding = W - plainLen;
|
|
985
|
+
return chalk.cyan('║') + ' ' + content + ' '.repeat(Math.max(0, padding - 1)) + chalk.cyan('║');
|
|
986
|
+
};
|
|
987
|
+
|
|
988
|
+
console.clear();
|
|
989
|
+
displayBanner();
|
|
990
|
+
drawBoxHeaderContinue(`SELECT MODEL - ${providerName}`, boxWidth);
|
|
991
|
+
|
|
992
|
+
if (!models || models.length === 0) {
|
|
993
|
+
console.log(makeLine(chalk.red('NO MODELS AVAILABLE')));
|
|
994
|
+
console.log(makeLine(chalk.gray('[<] BACK')));
|
|
995
|
+
drawBoxFooter(boxWidth);
|
|
996
|
+
await prompts.waitForEnter();
|
|
997
|
+
return null;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
// Sort models (newest first)
|
|
1001
|
+
const sortedModels = [...models].sort((a, b) => b.localeCompare(a));
|
|
1002
|
+
|
|
1003
|
+
// Display models from API
|
|
1004
|
+
sortedModels.forEach((model, index) => {
|
|
1005
|
+
const displayModel = model.length > W - 10 ? model.substring(0, W - 13) + '...' : model;
|
|
1006
|
+
console.log(makeLine(chalk.cyan(`[${index + 1}] ${displayModel}`)));
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
console.log(makeLine(''));
|
|
1010
|
+
console.log(makeLine(chalk.gray('[<] BACK')));
|
|
1011
|
+
|
|
1012
|
+
drawBoxFooter(boxWidth);
|
|
1013
|
+
|
|
1014
|
+
const choice = await prompts.textInput(chalk.cyan('SELECT MODEL:'));
|
|
1015
|
+
|
|
1016
|
+
if (choice === '<' || choice?.toLowerCase() === 'b') {
|
|
1017
|
+
return null;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
const index = parseInt(choice) - 1;
|
|
1021
|
+
if (isNaN(index) || index < 0 || index >= sortedModels.length) {
|
|
1022
|
+
return await selectModelFromList(models, providerName);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
return sortedModels[index];
|
|
1026
|
+
};
|
|
1027
|
+
|
|
945
1028
|
/**
|
|
946
1029
|
* Select/change model for an agent
|
|
1030
|
+
* Fetches available models from the provider's API
|
|
947
1031
|
*/
|
|
948
1032
|
const selectModel = async (agent) => {
|
|
949
1033
|
const boxWidth = getLogoWidth();
|
|
@@ -959,26 +1043,44 @@ const selectModel = async (agent) => {
|
|
|
959
1043
|
displayBanner();
|
|
960
1044
|
drawBoxHeaderContinue(`SELECT MODEL - ${agent.name}`, boxWidth);
|
|
961
1045
|
|
|
962
|
-
|
|
1046
|
+
console.log(makeLine(chalk.gray('FETCHING AVAILABLE MODELS FROM API...')));
|
|
1047
|
+
drawBoxFooter(boxWidth);
|
|
1048
|
+
|
|
1049
|
+
// Fetch models from real API
|
|
1050
|
+
const { fetchAnthropicModels, fetchOpenAIModels } = require('../services/ai/client');
|
|
1051
|
+
|
|
1052
|
+
let models = null;
|
|
1053
|
+
const agentCredentials = aiService.getAgentCredentials(agent.id);
|
|
963
1054
|
|
|
964
|
-
if (
|
|
965
|
-
|
|
1055
|
+
if (agent.providerId === 'anthropic') {
|
|
1056
|
+
models = await fetchAnthropicModels(agentCredentials?.apiKey);
|
|
1057
|
+
} else {
|
|
1058
|
+
// OpenAI-compatible providers
|
|
1059
|
+
const endpoint = agentCredentials?.endpoint || agent.provider?.endpoint;
|
|
1060
|
+
models = await fetchOpenAIModels(endpoint, agentCredentials?.apiKey);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// Redraw with results
|
|
1064
|
+
console.clear();
|
|
1065
|
+
displayBanner();
|
|
1066
|
+
drawBoxHeaderContinue(`SELECT MODEL - ${agent.name}`, boxWidth);
|
|
1067
|
+
|
|
1068
|
+
if (!models || models.length === 0) {
|
|
1069
|
+
console.log(makeLine(chalk.red('COULD NOT FETCH MODELS FROM API')));
|
|
1070
|
+
console.log(makeLine(chalk.gray('Check your API key or network connection.')));
|
|
966
1071
|
console.log(makeLine(''));
|
|
967
1072
|
console.log(makeLine(chalk.gray('[<] BACK')));
|
|
968
1073
|
drawBoxFooter(boxWidth);
|
|
969
1074
|
|
|
970
|
-
const model = await prompts.textInput('ENTER MODEL NAME (OR < TO GO BACK):');
|
|
971
|
-
if (!model || model === '<') {
|
|
972
|
-
return await aiAgentMenu();
|
|
973
|
-
}
|
|
974
|
-
aiService.updateAgent(agent.id, { model });
|
|
975
|
-
console.log(chalk.green(`\n MODEL CHANGED TO: ${model}`));
|
|
976
1075
|
await prompts.waitForEnter();
|
|
977
1076
|
return await aiAgentMenu();
|
|
978
1077
|
}
|
|
979
1078
|
|
|
1079
|
+
// Sort models (newest first typically)
|
|
1080
|
+
models.sort((a, b) => b.localeCompare(a));
|
|
1081
|
+
|
|
1082
|
+
// Display models from API
|
|
980
1083
|
models.forEach((model, index) => {
|
|
981
|
-
// Truncate long model names
|
|
982
1084
|
const displayModel = model.length > W - 10 ? model.substring(0, W - 13) + '...' : model;
|
|
983
1085
|
const currentMarker = model === agent.model ? chalk.yellow(' (CURRENT)') : '';
|
|
984
1086
|
console.log(makeLine(chalk.cyan(`[${index + 1}] ${displayModel}`) + currentMarker));
|
|
@@ -995,22 +1097,6 @@ const selectModel = async (agent) => {
|
|
|
995
1097
|
return await aiAgentMenu();
|
|
996
1098
|
}
|
|
997
1099
|
|
|
998
|
-
// Custom model option
|
|
999
|
-
if (choice?.toLowerCase() === 'c') {
|
|
1000
|
-
console.log(chalk.gray('\n Enter any model name supported by your provider.'));
|
|
1001
|
-
console.log(chalk.gray(' Examples: claude-opus-4-20250514, gpt-4o-2024-11-20, etc.\n'));
|
|
1002
|
-
|
|
1003
|
-
const customModel = await prompts.textInput(chalk.cyan('ENTER MODEL NAME:'));
|
|
1004
|
-
if (!customModel || customModel === '<') {
|
|
1005
|
-
return await selectModel(agent);
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
aiService.updateAgent(agent.id, { model: customModel.trim() });
|
|
1009
|
-
console.log(chalk.green(`\n MODEL CHANGED TO: ${customModel.trim()}`));
|
|
1010
|
-
await prompts.waitForEnter();
|
|
1011
|
-
return await aiAgentMenu();
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
1100
|
const index = parseInt(choice) - 1;
|
|
1015
1101
|
if (isNaN(index) || index < 0 || index >= models.length) {
|
|
1016
1102
|
return await selectModel(agent);
|
|
@@ -272,10 +272,72 @@ Analyze and provide recommendation.`;
|
|
|
272
272
|
}
|
|
273
273
|
};
|
|
274
274
|
|
|
275
|
+
/**
|
|
276
|
+
* Fetch available models from Anthropic API
|
|
277
|
+
* @param {string} apiKey - API key
|
|
278
|
+
* @returns {Promise<Array|null>} Array of model IDs or null on error
|
|
279
|
+
*
|
|
280
|
+
* Data source: https://api.anthropic.com/v1/models (GET)
|
|
281
|
+
*/
|
|
282
|
+
const fetchAnthropicModels = async (apiKey) => {
|
|
283
|
+
if (!apiKey) return null;
|
|
284
|
+
|
|
285
|
+
const url = 'https://api.anthropic.com/v1/models';
|
|
286
|
+
|
|
287
|
+
const headers = {
|
|
288
|
+
'x-api-key': apiKey,
|
|
289
|
+
'anthropic-version': '2023-06-01'
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
const response = await makeRequest(url, { method: 'GET', headers, timeout: 10000 });
|
|
294
|
+
if (response.data && Array.isArray(response.data)) {
|
|
295
|
+
return response.data.map(m => m.id).filter(Boolean);
|
|
296
|
+
}
|
|
297
|
+
return null;
|
|
298
|
+
} catch (error) {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Fetch available models from OpenAI-compatible API
|
|
305
|
+
* @param {string} endpoint - API endpoint
|
|
306
|
+
* @param {string} apiKey - API key
|
|
307
|
+
* @returns {Promise<Array|null>} Array of model IDs or null on error
|
|
308
|
+
*
|
|
309
|
+
* Data source: {endpoint}/models (GET)
|
|
310
|
+
*/
|
|
311
|
+
const fetchOpenAIModels = async (endpoint, apiKey) => {
|
|
312
|
+
if (!endpoint) return null;
|
|
313
|
+
|
|
314
|
+
const url = `${endpoint}/models`;
|
|
315
|
+
|
|
316
|
+
const headers = {
|
|
317
|
+
'Content-Type': 'application/json'
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
if (apiKey) {
|
|
321
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
const response = await makeRequest(url, { method: 'GET', headers, timeout: 10000 });
|
|
326
|
+
if (response.data && Array.isArray(response.data)) {
|
|
327
|
+
return response.data.map(m => m.id).filter(Boolean);
|
|
328
|
+
}
|
|
329
|
+
return null;
|
|
330
|
+
} catch (error) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
|
|
275
335
|
module.exports = {
|
|
276
336
|
callAI,
|
|
277
337
|
analyzeTrading,
|
|
278
338
|
callOpenAICompatible,
|
|
279
339
|
callAnthropic,
|
|
280
|
-
callGemini
|
|
340
|
+
callGemini,
|
|
341
|
+
fetchAnthropicModels,
|
|
342
|
+
fetchOpenAIModels
|
|
281
343
|
};
|
package/src/services/ai/index.js
CHANGED
|
@@ -159,6 +159,20 @@ const getAgent = (agentId) => {
|
|
|
159
159
|
};
|
|
160
160
|
};
|
|
161
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Get agent credentials by ID
|
|
164
|
+
* @param {string} agentId - Agent ID
|
|
165
|
+
* @returns {Object|null} Credentials object or null
|
|
166
|
+
*/
|
|
167
|
+
const getAgentCredentials = (agentId) => {
|
|
168
|
+
const aiSettings = getAISettings();
|
|
169
|
+
const agents = aiSettings.agents || [];
|
|
170
|
+
const agent = agents.find(a => a.id === agentId);
|
|
171
|
+
|
|
172
|
+
if (!agent) return null;
|
|
173
|
+
return agent.credentials || null;
|
|
174
|
+
};
|
|
175
|
+
|
|
162
176
|
/**
|
|
163
177
|
* Set active agent
|
|
164
178
|
*/
|
|
@@ -607,6 +621,7 @@ module.exports = {
|
|
|
607
621
|
getAgents,
|
|
608
622
|
getAgentCount,
|
|
609
623
|
getAgent,
|
|
624
|
+
getAgentCredentials,
|
|
610
625
|
getActiveAgent,
|
|
611
626
|
setActiveAgent,
|
|
612
627
|
addAgent,
|
|
@@ -43,21 +43,8 @@ const PROVIDERS = {
|
|
|
43
43
|
name: 'CLAUDE (ANTHROPIC)',
|
|
44
44
|
description: 'Direct connection to Claude',
|
|
45
45
|
category: 'direct',
|
|
46
|
-
models: [
|
|
47
|
-
|
|
48
|
-
'claude-opus-4-5-20250514',
|
|
49
|
-
// Claude 4
|
|
50
|
-
'claude-opus-4-20250514',
|
|
51
|
-
'claude-sonnet-4-20250514',
|
|
52
|
-
// Claude 3.5
|
|
53
|
-
'claude-3-5-sonnet-20241022',
|
|
54
|
-
'claude-3-5-haiku-20241022',
|
|
55
|
-
// Claude 3
|
|
56
|
-
'claude-3-opus-20240229',
|
|
57
|
-
'claude-3-sonnet-20240229',
|
|
58
|
-
'claude-3-haiku-20240307'
|
|
59
|
-
],
|
|
60
|
-
defaultModel: 'claude-opus-4-5-20250514',
|
|
46
|
+
models: [], // Fetched from API at runtime
|
|
47
|
+
defaultModel: null, // Will use first model from API
|
|
61
48
|
options: [
|
|
62
49
|
{
|
|
63
50
|
id: 'api_key',
|