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.
@@ -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);