omnigate-ai 1.0.0
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/Dockerfile +37 -0
- package/Omnigate AI.txt +72 -0
- package/bin/omnigate.js +2 -0
- package/docker-compose.yml +16 -0
- package/package.json +30 -0
- package/payload-theme.json +1 -0
- package/proxy-helper.js +60 -0
- package/public/app.js +432 -0
- package/public/index.html +367 -0
- package/public/styles.css +1060 -0
- package/server.js +586 -0
- package/test-payload.json +1 -0
- package/test-payload2.json +1 -0
- package/test-real-api.js +228 -0
package/test-real-api.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OmniGate AI - Real API Test Script
|
|
3
|
+
*
|
|
4
|
+
* This script:
|
|
5
|
+
* 1. Configures the proxy with your real API key
|
|
6
|
+
* 2. Sends prompts about THIS project (the server.js code)
|
|
7
|
+
* 3. Captures and displays exact token usage & cost from the proxy telemetry
|
|
8
|
+
*
|
|
9
|
+
* Usage: node test-real-api.js
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import readline from 'readline';
|
|
13
|
+
|
|
14
|
+
const rl = readline.createInterface({
|
|
15
|
+
input: process.stdin,
|
|
16
|
+
output: process.stdout
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
function askQuestion(query) {
|
|
20
|
+
return new Promise(resolve => rl.question(query, resolve));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Pricing for cost calculation (matches the dashboard)
|
|
24
|
+
const PRICING = {
|
|
25
|
+
'gpt-4o': { in: 5.00, out: 15.00 },
|
|
26
|
+
'gpt-4o-mini': { in: 0.15, out: 0.60 },
|
|
27
|
+
'gpt-4o-2024-08-06': { in: 2.50, out: 10.00 },
|
|
28
|
+
'gpt-4-turbo': { in: 10.00, out: 30.00 },
|
|
29
|
+
'gpt-3.5-turbo': { in: 0.50, out: 1.50 },
|
|
30
|
+
'claude-3-5-sonnet-20241022': { in: 3.00, out: 15.00 },
|
|
31
|
+
'claude-3-opus-20240229': { in: 15.00, out: 75.00 },
|
|
32
|
+
'claude-3-haiku-20240307': { in: 0.25, out: 1.25 },
|
|
33
|
+
'default': { in: 2.00, out: 10.00 }
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function calculateCost(model, inTokens, outTokens) {
|
|
37
|
+
const rates = PRICING[model] || PRICING['default'];
|
|
38
|
+
return (inTokens / 1000000) * rates.in + (outTokens / 1000000) * rates.out;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// The server.js code content - this is what we'll ask about
|
|
42
|
+
const SERVER_CODE_INTRO = `
|
|
43
|
+
The OmniGate server.js is an Express-based proxy server with these key features:
|
|
44
|
+
- Proxies requests to OpenAI (/v1/chat/completions) and Anthropic (/v1/messages)
|
|
45
|
+
- Intercepts streaming SSE responses to extract usage/token metadata
|
|
46
|
+
- Forces OpenAI to include usage data via stream_options: {include_usage: true}
|
|
47
|
+
- Parses Anthropic message_start/message_delta events for token counting
|
|
48
|
+
- Logs telemetry with provider, model, input_tokens, output_tokens, userId, projectId
|
|
49
|
+
- Has a dashboard at /dashboard for live telemetry viewing
|
|
50
|
+
- Requires X-Gateway-Key header for authentication
|
|
51
|
+
- Uses timingSafeEqual for header comparison to prevent timing attacks
|
|
52
|
+
- Handles client disconnects by aborting upstream requests
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
async function main() {
|
|
56
|
+
console.log('\n╔══════════════════════════════════════════════════╗');
|
|
57
|
+
console.log('║ 🧪 OmniGate AI - Real API Token Test ║');
|
|
58
|
+
console.log('║ Testing prompt costs against THIS project ║');
|
|
59
|
+
console.log('╚══════════════════════════════════════════════════╝\n');
|
|
60
|
+
|
|
61
|
+
// Get API key from user (masked input would require pty, so plain for now)
|
|
62
|
+
const apiKey = await askQuestion('🔑 Enter your OpenAI API key (sk-...): ');
|
|
63
|
+
|
|
64
|
+
// Choose model
|
|
65
|
+
console.log('\n📋 Available models:');
|
|
66
|
+
console.log(' 1. gpt-4o (most capable)');
|
|
67
|
+
console.log(' 2. gpt-4o-mini (cheapest)');
|
|
68
|
+
console.log(' 3. gpt-4-turbo');
|
|
69
|
+
console.log(' 4. gpt-3.5-turbo');
|
|
70
|
+
const modelChoice = await askQuestion('\n🎯 Choose model (1-4, default: 1): ') || '1';
|
|
71
|
+
const models = ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-3.5-turbo'];
|
|
72
|
+
const model = models[parseInt(modelChoice) - 1] || 'gpt-4o';
|
|
73
|
+
|
|
74
|
+
// Choose what to ask about
|
|
75
|
+
console.log('\n📝 Test prompts about this project:');
|
|
76
|
+
console.log(' 1. "Explain the architecture of this proxy server"');
|
|
77
|
+
console.log(' 2. "How does the SSE token interception work?"');
|
|
78
|
+
console.log(' 3. "Find any bugs or security issues in this code"');
|
|
79
|
+
console.log(' 4. "Write me a comprehensive code review"');
|
|
80
|
+
console.log(' 5. All of the above (multiple requests)');
|
|
81
|
+
const promptChoice = await askQuestion('\n🎯 Choose prompt (1-5, default: 5): ') || '5';
|
|
82
|
+
|
|
83
|
+
const prompts = {
|
|
84
|
+
1: `${SERVER_CODE_INTRO}\n\nExplain the architecture of this proxy server. What are its main components and how do they interact?`,
|
|
85
|
+
2: `${SERVER_CODE_INTRO}\n\nExplain in detail how the SSE (Server-Sent Events) token interception works for both OpenAI and Anthropic streaming responses. How does it extract usage metadata?`,
|
|
86
|
+
3: `${SERVER_CODE_INTRO}\n\nReview this code carefully. Are there any bugs, security vulnerabilities, or edge cases that aren't handled properly? Be specific.`,
|
|
87
|
+
4: `${SERVER_CODE_INTRO}\n\nProvide a comprehensive code review of this server.js file. Cover architecture, error handling, security, performance, and suggest improvements.`
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Configure the proxy with the API key
|
|
91
|
+
console.log('\n⚙️ Configuring proxy with API key...');
|
|
92
|
+
const configRes = await fetch('http://localhost:8080/api/config', {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers: {
|
|
95
|
+
'Content-Type': 'application/json',
|
|
96
|
+
'X-Gateway-Key': 'stub-agency-key-for-local-testing'
|
|
97
|
+
},
|
|
98
|
+
body: JSON.stringify({ openaiApiKey: apiKey })
|
|
99
|
+
});
|
|
100
|
+
const configData = await configRes.json();
|
|
101
|
+
if (configData.status !== 'success') {
|
|
102
|
+
console.error('❌ Failed to configure proxy:', configData);
|
|
103
|
+
rl.close();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
console.log('✅ Proxy configured successfully!\n');
|
|
107
|
+
|
|
108
|
+
// Clear previous telemetry
|
|
109
|
+
console.log('📊 Clearing previous telemetry data...');
|
|
110
|
+
|
|
111
|
+
// Determine which prompts to run
|
|
112
|
+
const selectedPrompts = (promptChoice === '5')
|
|
113
|
+
? [1, 2, 3, 4]
|
|
114
|
+
: [parseInt(promptChoice)];
|
|
115
|
+
|
|
116
|
+
const allTelemetry = [];
|
|
117
|
+
|
|
118
|
+
for (const idx of selectedPrompts) {
|
|
119
|
+
const prompt = prompts[idx];
|
|
120
|
+
const promptName = {
|
|
121
|
+
1: 'Architecture explanation',
|
|
122
|
+
2: 'SSE token interception',
|
|
123
|
+
3: 'Bug & security review',
|
|
124
|
+
4: 'Comprehensive code review'
|
|
125
|
+
}[idx];
|
|
126
|
+
|
|
127
|
+
console.log('──────────────────────────────────────────────────────');
|
|
128
|
+
console.log(`📤 Sending: ${promptName}`);
|
|
129
|
+
console.log('──────────────────────────────────────────────────────');
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const startTime = Date.now();
|
|
133
|
+
|
|
134
|
+
const response = await fetch('http://localhost:8080/v1/chat/completions', {
|
|
135
|
+
method: 'POST',
|
|
136
|
+
headers: {
|
|
137
|
+
'Content-Type': 'application/json',
|
|
138
|
+
'X-Gateway-Key': 'stub-agency-key-for-local-testing',
|
|
139
|
+
'X-Gateway-User-ID': 'erfan',
|
|
140
|
+
'X-Gateway-Project-ID': 'omnigate-ai-project'
|
|
141
|
+
},
|
|
142
|
+
body: JSON.stringify({
|
|
143
|
+
model: model,
|
|
144
|
+
messages: [{ role: 'user', content: prompt }],
|
|
145
|
+
stream: false
|
|
146
|
+
})
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
const errText = await response.text();
|
|
151
|
+
console.error(`❌ Request failed (${response.status}): ${errText}`);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const data = await response.json();
|
|
156
|
+
const elapsed = Date.now() - startTime;
|
|
157
|
+
|
|
158
|
+
// Read telemetry from the proxy
|
|
159
|
+
await new Promise(r => setTimeout(r, 200));
|
|
160
|
+
const telemetryRes = await fetch('http://localhost:8080/api/telemetry');
|
|
161
|
+
const telemetryData = await telemetryRes.json();
|
|
162
|
+
|
|
163
|
+
// Find the latest telemetry entry
|
|
164
|
+
const latestTel = telemetryData[0];
|
|
165
|
+
|
|
166
|
+
if (latestTel && latestTel.inputTokens > 0) {
|
|
167
|
+
const cost = calculateCost(latestTel.model, latestTel.inputTokens, latestTel.outputTokens);
|
|
168
|
+
allTelemetry.push(latestTel);
|
|
169
|
+
|
|
170
|
+
const content = data.choices?.[0]?.message?.content || '';
|
|
171
|
+
const preview = content.length > 200 ? content.substring(0, 200) + '...' : content;
|
|
172
|
+
|
|
173
|
+
console.log(` ✅ Response received in ${elapsed}ms`);
|
|
174
|
+
console.log(` 📝 Preview: "${preview}"\n`);
|
|
175
|
+
console.log(` 📊 TOKEN USAGE:`);
|
|
176
|
+
console.log(` Input Tokens : ${latestTel.inputTokens.toLocaleString()}`);
|
|
177
|
+
console.log(` Output Tokens: ${latestTel.outputTokens.toLocaleString()}`);
|
|
178
|
+
console.log(` Total Tokens : ${(latestTel.inputTokens + latestTel.outputTokens).toLocaleString()}`);
|
|
179
|
+
console.log(` 💰 Cost : $${cost.toFixed(6)}`);
|
|
180
|
+
} else {
|
|
181
|
+
console.log(' ⚠️ Response received but no telemetry found.');
|
|
182
|
+
}
|
|
183
|
+
} catch (err) {
|
|
184
|
+
console.error(`❌ Error: ${err.message}`);
|
|
185
|
+
}
|
|
186
|
+
console.log('');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Print summary
|
|
190
|
+
console.log('\n╔══════════════════════════════════════════════════╗');
|
|
191
|
+
console.log('║ 📈 FINAL CONSOLIDATED REPORT ║');
|
|
192
|
+
console.log('╚══════════════════════════════════════════════════╝\n');
|
|
193
|
+
|
|
194
|
+
if (allTelemetry.length === 0) {
|
|
195
|
+
console.log('No telemetry data captured. Is the proxy running?');
|
|
196
|
+
rl.close();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
let totalInput = 0, totalOutput = 0, totalCost = 0;
|
|
201
|
+
console.log(` Model Used: ${model}`);
|
|
202
|
+
for (const tel of allTelemetry) {
|
|
203
|
+
const cost = calculateCost(tel.model, tel.inputTokens, tel.outputTokens);
|
|
204
|
+
totalInput += tel.inputTokens;
|
|
205
|
+
totalOutput += tel.outputTokens;
|
|
206
|
+
totalCost += cost;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
console.log(` Total Requests : ${allTelemetry.length}`);
|
|
210
|
+
console.log(` Total Input : ${totalInput.toLocaleString()} tokens`);
|
|
211
|
+
console.log(` Total Output : ${totalOutput.toLocaleString()} tokens`);
|
|
212
|
+
console.log(` Grand Total : ${(totalInput + totalOutput).toLocaleString()} tokens`);
|
|
213
|
+
console.log(` 💰 Total Cost : $${totalCost.toFixed(6)}`);
|
|
214
|
+
|
|
215
|
+
// Estimate for 1000 similar requests
|
|
216
|
+
console.log(`\n 📊 ESTIMATES:`);
|
|
217
|
+
console.log(` Cost per request : $${(totalCost / allTelemetry.length).toFixed(6)}`);
|
|
218
|
+
console.log(` Est. cost for 1000 reqs : $${((totalCost / allTelemetry.length) * 1000).toFixed(4)}`);
|
|
219
|
+
console.log(` Est. cost for 10000 reqs : $${((totalCost / allTelemetry.length) * 10000).toFixed(4)}`);
|
|
220
|
+
console.log(` Est. cost for 100k reqs : $${((totalCost / allTelemetry.length) * 100000).toFixed(4)}`);
|
|
221
|
+
|
|
222
|
+
console.log(`\n🌐 View full details: http://localhost:8080/dashboard/`);
|
|
223
|
+
console.log('🔑 Gateway Key: stub-agency-key-for-local-testing\n');
|
|
224
|
+
|
|
225
|
+
rl.close();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
main().catch(console.error);
|