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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgequantx",
3
- "version": "2.5.22",
3
+ "version": "2.5.23",
4
4
  "description": "HedgeQuantX - Prop Futures Trading CLI",
5
5
  "main": "src/app.js",
6
6
  "bin": {
@@ -494,14 +494,39 @@ const showExistingTokens = async () => {
494
494
  return await showExistingTokens();
495
495
  }
496
496
 
497
- // Add as new agent
498
- const model = provider.defaultModel;
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, model, agentName);
525
+ await aiService.addAgent(selectedToken.provider, 'api_key', credentials, selectedModel, agentName);
501
526
 
502
- spinner.succeed(`AGENT ADDED: ${provider.name}`);
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: ${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
- const models = agent.provider?.models || [];
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 (models.length === 0) {
965
- console.log(makeLine(chalk.gray('NO PREDEFINED MODELS. ENTER MODEL NAME MANUALLY.')));
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
  };
@@ -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
- // Claude 4.5 (Latest flagship)
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',