hedgequantx 2.5.21 → 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 +118 -16
- package/src/services/ai/client.js +63 -1
- package/src/services/ai/index.js +15 -0
- package/src/services/ai/providers/index.js +2 -10
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);
|
|
1054
|
+
|
|
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);
|
|
963
1067
|
|
|
964
|
-
if (models.length === 0) {
|
|
965
|
-
console.log(makeLine(chalk.
|
|
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));
|
|
@@ -997,7 +1099,7 @@ const selectModel = async (agent) => {
|
|
|
997
1099
|
|
|
998
1100
|
const index = parseInt(choice) - 1;
|
|
999
1101
|
if (isNaN(index) || index < 0 || index >= models.length) {
|
|
1000
|
-
return await
|
|
1102
|
+
return await selectModel(agent);
|
|
1001
1103
|
}
|
|
1002
1104
|
|
|
1003
1105
|
const selectedModel = models[index];
|
|
@@ -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,16 +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-sonnet-4-20250514', // Claude Sonnet 4
|
|
49
|
-
'claude-sonnet-4-5-20250929', // Claude Sonnet 4.5 (extended thinking)
|
|
50
|
-
'claude-3-5-sonnet-20241022', // Claude 3.5 Sonnet v2
|
|
51
|
-
'claude-3-5-haiku-20241022', // Claude 3.5 Haiku
|
|
52
|
-
'claude-3-opus-20240229', // Claude 3 Opus
|
|
53
|
-
'claude-3-haiku-20240307' // Claude 3 Haiku
|
|
54
|
-
],
|
|
55
|
-
defaultModel: 'claude-sonnet-4-20250514',
|
|
46
|
+
models: [], // Fetched from API at runtime
|
|
47
|
+
defaultModel: null, // Will use first model from API
|
|
56
48
|
options: [
|
|
57
49
|
{
|
|
58
50
|
id: 'api_key',
|