hedgequantx 2.9.3 → 2.9.4
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
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
* Verifies that AI agents are properly connected and responding
|
|
5
5
|
* before allowing algo trading with AI supervision.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Supports both connection types:
|
|
8
|
+
* - CLIProxy (OAuth) for Anthropic, OpenAI, Google, Qwen, iFlow
|
|
9
|
+
* - Direct API Key for MiniMax, DeepSeek, Mistral, xAI, OpenRouter
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
const cliproxy = require('../cliproxy');
|
|
13
|
+
const https = require('https');
|
|
13
14
|
|
|
14
15
|
/** Test prompt to verify agent understands directive format */
|
|
15
16
|
const TEST_PROMPT = `You are being tested. Respond ONLY with this exact JSON, nothing else:
|
|
@@ -39,28 +40,105 @@ const checkCliproxyRunning = async () => {
|
|
|
39
40
|
}
|
|
40
41
|
};
|
|
41
42
|
|
|
43
|
+
/**
|
|
44
|
+
* API endpoints for direct API key providers
|
|
45
|
+
*/
|
|
46
|
+
const API_CHAT_ENDPOINTS = {
|
|
47
|
+
minimax: 'https://api.minimax.io/v1/chat/completions',
|
|
48
|
+
deepseek: 'https://api.deepseek.com/v1/chat/completions',
|
|
49
|
+
mistral: 'https://api.mistral.ai/v1/chat/completions',
|
|
50
|
+
xai: 'https://api.x.ai/v1/chat/completions',
|
|
51
|
+
openrouter: 'https://openrouter.ai/api/v1/chat/completions',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Test connection via direct API key
|
|
56
|
+
* @param {Object} agent - Agent config
|
|
57
|
+
* @returns {Promise<Object>} { success, latency, formatValid, error }
|
|
58
|
+
*/
|
|
59
|
+
const testApiKeyConnection = (agent) => {
|
|
60
|
+
return new Promise((resolve) => {
|
|
61
|
+
const startTime = Date.now();
|
|
62
|
+
const endpoint = API_CHAT_ENDPOINTS[agent.provider];
|
|
63
|
+
|
|
64
|
+
if (!endpoint || !agent.apiKey) {
|
|
65
|
+
resolve({ success: false, latency: 0, formatValid: false, error: 'Missing endpoint or API key' });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const url = new URL(endpoint);
|
|
70
|
+
const body = JSON.stringify({
|
|
71
|
+
model: agent.modelId,
|
|
72
|
+
messages: [{ role: 'user', content: TEST_PROMPT }],
|
|
73
|
+
max_tokens: 100
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const options = {
|
|
77
|
+
hostname: url.hostname,
|
|
78
|
+
path: url.pathname,
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: {
|
|
81
|
+
'Content-Type': 'application/json',
|
|
82
|
+
'Authorization': `Bearer ${agent.apiKey}`,
|
|
83
|
+
'Content-Length': Buffer.byteLength(body)
|
|
84
|
+
},
|
|
85
|
+
timeout: AGENT_TIMEOUT
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const req = https.request(options, (res) => {
|
|
89
|
+
let data = '';
|
|
90
|
+
res.on('data', chunk => data += chunk);
|
|
91
|
+
res.on('end', () => {
|
|
92
|
+
const latency = Date.now() - startTime;
|
|
93
|
+
try {
|
|
94
|
+
const parsed = JSON.parse(data);
|
|
95
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
96
|
+
const content = parsed.choices?.[0]?.message?.content || '';
|
|
97
|
+
const formatResult = validateResponseFormat(content);
|
|
98
|
+
resolve({
|
|
99
|
+
success: formatResult.valid,
|
|
100
|
+
latency,
|
|
101
|
+
formatValid: formatResult.valid,
|
|
102
|
+
error: formatResult.valid ? null : formatResult.error,
|
|
103
|
+
response: content
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
resolve({ success: false, latency, formatValid: false,
|
|
107
|
+
error: parsed.error?.message || `HTTP ${res.statusCode}` });
|
|
108
|
+
}
|
|
109
|
+
} catch (e) {
|
|
110
|
+
resolve({ success: false, latency, formatValid: false, error: 'Invalid response' });
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
req.on('error', (e) => resolve({
|
|
116
|
+
success: false, latency: Date.now() - startTime, formatValid: false, error: e.message
|
|
117
|
+
}));
|
|
118
|
+
req.on('timeout', () => {
|
|
119
|
+
req.destroy();
|
|
120
|
+
resolve({ success: false, latency: AGENT_TIMEOUT, formatValid: false, error: 'Timeout' });
|
|
121
|
+
});
|
|
122
|
+
req.write(body);
|
|
123
|
+
req.end();
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
|
|
42
127
|
/**
|
|
43
128
|
* Test a single agent connection and response format
|
|
44
|
-
* @param {Object} agent - Agent config { id, provider, modelId, connectionType, ... }
|
|
129
|
+
* @param {Object} agent - Agent config { id, provider, modelId, connectionType, apiKey, ... }
|
|
45
130
|
* @returns {Promise<Object>} { success, latency, formatValid, error }
|
|
46
131
|
*/
|
|
47
132
|
const testAgentConnection = async (agent) => {
|
|
48
133
|
const startTime = Date.now();
|
|
49
134
|
|
|
50
135
|
try {
|
|
51
|
-
//
|
|
52
|
-
if (agent.connectionType
|
|
53
|
-
|
|
54
|
-
// For now, mark as needing CLIProxy
|
|
55
|
-
return {
|
|
56
|
-
success: false,
|
|
57
|
-
latency: 0,
|
|
58
|
-
formatValid: false,
|
|
59
|
-
error: 'Only CLIProxy connections supported for pre-check'
|
|
60
|
-
};
|
|
136
|
+
// Route based on connection type
|
|
137
|
+
if (agent.connectionType === 'apikey') {
|
|
138
|
+
return await testApiKeyConnection(agent);
|
|
61
139
|
}
|
|
62
140
|
|
|
63
|
-
//
|
|
141
|
+
// CLIProxy connection
|
|
64
142
|
const result = await cliproxy.chat(agent.provider, agent.modelId, TEST_PROMPT, AGENT_TIMEOUT);
|
|
65
143
|
const latency = Date.now() - startTime;
|
|
66
144
|
|
|
@@ -156,29 +234,53 @@ const validateResponseFormat = (content) => {
|
|
|
156
234
|
/**
|
|
157
235
|
* Run pre-flight check on all agents
|
|
158
236
|
* @param {Array} agents - Array of agent configs
|
|
159
|
-
* @returns {Promise<Object>} { success, cliproxy, agents, summary }
|
|
237
|
+
* @returns {Promise<Object>} { success, cliproxy, needsCliproxy, agents, summary }
|
|
160
238
|
*/
|
|
161
239
|
const runPreflightCheck = async (agents) => {
|
|
162
240
|
const results = {
|
|
163
241
|
success: false,
|
|
164
242
|
cliproxy: null,
|
|
243
|
+
needsCliproxy: false,
|
|
165
244
|
agents: [],
|
|
166
245
|
summary: { total: 0, passed: 0, failed: 0 }
|
|
167
246
|
};
|
|
168
247
|
|
|
169
|
-
//
|
|
170
|
-
results.
|
|
248
|
+
// Check if any agent needs CLIProxy (non-apikey connection)
|
|
249
|
+
results.needsCliproxy = agents.some(a => a.connectionType !== 'apikey');
|
|
171
250
|
|
|
172
|
-
if
|
|
173
|
-
|
|
174
|
-
results.
|
|
175
|
-
|
|
251
|
+
// Step 1: Check CLIProxy only if needed
|
|
252
|
+
if (results.needsCliproxy) {
|
|
253
|
+
results.cliproxy = await checkCliproxyRunning();
|
|
254
|
+
if (!results.cliproxy.success) {
|
|
255
|
+
// Mark only CLIProxy agents as failed, still test API Key agents
|
|
256
|
+
results.summary.total = agents.length;
|
|
257
|
+
}
|
|
258
|
+
} else {
|
|
259
|
+
// No CLIProxy needed, mark as success
|
|
260
|
+
results.cliproxy = { success: true, latency: 0, error: null, notNeeded: true };
|
|
176
261
|
}
|
|
177
262
|
|
|
178
263
|
// Step 2: Test each agent
|
|
179
264
|
results.summary.total = agents.length;
|
|
180
265
|
|
|
181
266
|
for (const agent of agents) {
|
|
267
|
+
// Skip CLIProxy agents if CLIProxy is not running
|
|
268
|
+
if (agent.connectionType !== 'apikey' && !results.cliproxy.success) {
|
|
269
|
+
results.agents.push({
|
|
270
|
+
id: agent.id,
|
|
271
|
+
name: agent.name,
|
|
272
|
+
provider: agent.provider,
|
|
273
|
+
modelId: agent.modelId,
|
|
274
|
+
connectionType: agent.connectionType,
|
|
275
|
+
success: false,
|
|
276
|
+
latency: 0,
|
|
277
|
+
formatValid: false,
|
|
278
|
+
error: 'CLIProxy not running'
|
|
279
|
+
});
|
|
280
|
+
results.summary.failed++;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
|
|
182
284
|
const agentResult = await testAgentConnection(agent);
|
|
183
285
|
|
|
184
286
|
results.agents.push({
|
|
@@ -186,6 +288,7 @@ const runPreflightCheck = async (agents) => {
|
|
|
186
288
|
name: agent.name,
|
|
187
289
|
provider: agent.provider,
|
|
188
290
|
modelId: agent.modelId,
|
|
291
|
+
connectionType: agent.connectionType,
|
|
189
292
|
...agentResult
|
|
190
293
|
});
|
|
191
294
|
|
|
@@ -221,13 +324,13 @@ const formatPreflightResults = (results, boxWidth) => {
|
|
|
221
324
|
return ' '.repeat(labelPad) + chalk.white(label) + chalk.gray('.'.repeat(Math.max(3, dotsLen))) + value;
|
|
222
325
|
};
|
|
223
326
|
|
|
224
|
-
// CLIProxy status
|
|
225
|
-
if (results.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
327
|
+
// CLIProxy status (only show if needed)
|
|
328
|
+
if (results.needsCliproxy) {
|
|
329
|
+
if (results.cliproxy.success) {
|
|
330
|
+
lines.push(dottedLine('CLIProxy Status', chalk.green('✓ RUNNING')));
|
|
331
|
+
} else {
|
|
332
|
+
lines.push(dottedLine('CLIProxy Status', chalk.red('✗ NOT RUNNING')));
|
|
333
|
+
}
|
|
231
334
|
}
|
|
232
335
|
|
|
233
336
|
lines.push('');
|
|
@@ -238,8 +341,9 @@ const formatPreflightResults = (results, boxWidth) => {
|
|
|
238
341
|
for (let i = 0; i < results.agents.length; i++) {
|
|
239
342
|
const agent = results.agents[i];
|
|
240
343
|
const num = `[${i + 1}/${results.summary.total}]`;
|
|
344
|
+
const connType = agent.connectionType === 'apikey' ? 'API Key' : 'OAuth';
|
|
241
345
|
|
|
242
|
-
lines.push(chalk.cyan(` ${num} ${agent.name} (${agent.modelId || agent.provider})`));
|
|
346
|
+
lines.push(chalk.cyan(` ${num} ${agent.name} (${agent.modelId || agent.provider}) [${connType}]`));
|
|
243
347
|
|
|
244
348
|
if (agent.success) {
|
|
245
349
|
const latencyStr = `✓ OK ${agent.latency}ms`;
|
|
@@ -264,9 +368,10 @@ const formatPreflightResults = (results, boxWidth) => {
|
|
|
264
368
|
const getPreflightSummary = (results) => {
|
|
265
369
|
const chalk = require('chalk');
|
|
266
370
|
|
|
267
|
-
if
|
|
371
|
+
// Only show CLIProxy error if it was needed and failed
|
|
372
|
+
if (results.needsCliproxy && !results.cliproxy.success) {
|
|
268
373
|
return {
|
|
269
|
-
text: chalk.red('✗ CLIProxy not running - cannot
|
|
374
|
+
text: chalk.red('✗ CLIProxy not running - some agents cannot be verified'),
|
|
270
375
|
success: false
|
|
271
376
|
};
|
|
272
377
|
}
|