71d14209a4
OmniRoute is an intelligent API gateway that unifies 20+ AI providers behind a single OpenAI-compatible endpoint. Features include intelligent routing with 6 strategies, multi-format translation (OpenAI/Claude/Gemini/Responses API), circuit breakers, semantic caching, combo fallback chains, real-time health monitoring, and a full dashboard with provider management, analytics, and CLI tool integration. Key highlights: - 20+ providers (Claude Code, Codex, Gemini CLI, GitHub Copilot, iFlow, Qwen, Kiro, etc.) - 6 routing strategies (Fill First, Round Robin, P2C, Random, Least Used, Cost Optimized) - Export/Import database backup with full archive support - Translator Playground with 4 modes (Playground, Chat Tester, Test Bench, Live Monitor) - 100% TypeScript across src/ and open-sse/ - Docker support with multi-stage builds - Comprehensive documentation and 9 dashboard screenshots
153 lines
4.1 KiB
JavaScript
Executable File
153 lines
4.1 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Test sending request from converted file directly to provider
|
|
* Usage:
|
|
* node testFromFile.js <file-path>
|
|
* node testFromFile.js data/claude-to-kiro/3_converted_request.json
|
|
*/
|
|
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
|
|
const args = process.argv.slice(2);
|
|
|
|
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
console.log("");
|
|
console.log("🧪 Test From File - Send converted request to provider");
|
|
console.log("");
|
|
console.log("Usage:");
|
|
console.log(" node testFromFile.js <file-path>");
|
|
console.log("");
|
|
console.log("Examples:");
|
|
console.log(" node testFromFile.js data/claude-to-kiro/3_converted_request.json");
|
|
console.log(" node testFromFile.js ../logs/openai_codex_xxx/3_converted_request.json");
|
|
console.log("");
|
|
console.log("File format:");
|
|
console.log(" {");
|
|
console.log(' "url": "https://api.provider.com/...",');
|
|
console.log(' "headers": { ... },');
|
|
console.log(' "body": { ... }');
|
|
console.log(" }");
|
|
console.log("");
|
|
process.exit(0);
|
|
}
|
|
|
|
const filePath = args[0];
|
|
const fullPath = path.isAbsolute(filePath) ? filePath : path.join(process.cwd(), filePath);
|
|
|
|
if (!fs.existsSync(fullPath)) {
|
|
console.error(`❌ File not found: ${fullPath}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
// Load request data
|
|
let data;
|
|
try {
|
|
data = JSON.parse(fs.readFileSync(fullPath, "utf8"));
|
|
} catch (err) {
|
|
console.error(`❌ Failed to parse JSON: ${err.message}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const { url, headers, body } = data;
|
|
|
|
if (!url || !headers || !body) {
|
|
console.error("❌ Invalid file format. Expected: { url, headers, body }");
|
|
process.exit(1);
|
|
}
|
|
|
|
// Display request info
|
|
console.log("\n🚀 Sending Request from File\n");
|
|
console.log(`📁 File: ${filePath}`);
|
|
console.log(`🌐 URL: ${url}`);
|
|
console.log(`📋 Headers:`);
|
|
Object.entries(headers).forEach(([k, v]) => {
|
|
if (
|
|
k.toLowerCase().includes("auth") ||
|
|
k.toLowerCase().includes("key") ||
|
|
k.toLowerCase().includes("bearer")
|
|
) {
|
|
const str = String(v);
|
|
if (str.length > 20) {
|
|
console.log(` ${k}: ${str.slice(0, 20)}...`);
|
|
} else {
|
|
console.log(` ${k}: ${str}`);
|
|
}
|
|
} else {
|
|
console.log(` ${k}: ${v}`);
|
|
}
|
|
});
|
|
|
|
console.log(`\n📊 Request Body:`);
|
|
console.log(` Model: ${body.model || "N/A"}`);
|
|
console.log(` Messages: ${body.messages?.length || 0}`);
|
|
console.log(` Tools: ${body.tools?.length || 0}`);
|
|
console.log(` Stream: ${body.stream || false}`);
|
|
|
|
// Send request
|
|
(async () => {
|
|
try {
|
|
console.log("\n🚀 Sending request...");
|
|
|
|
const response = await fetch(url, {
|
|
method: "POST",
|
|
headers,
|
|
body: JSON.stringify(body),
|
|
});
|
|
|
|
console.log(`\n📥 Response: ${response.status} ${response.statusText}`);
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
console.error(`\n❌ Error response:\n${errorText}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const isStreaming =
|
|
body.stream || response.headers.get("content-type")?.includes("text/event-stream");
|
|
|
|
if (isStreaming) {
|
|
console.log("\n📡 Streaming response...\n");
|
|
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder();
|
|
let chunkCount = 0;
|
|
let buffer = "";
|
|
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
|
|
buffer += decoder.decode(value, { stream: true });
|
|
const lines = buffer.split("\n");
|
|
buffer = lines.pop(); // Keep incomplete line in buffer
|
|
|
|
for (const line of lines) {
|
|
if (line.trim()) {
|
|
process.stdout.write(line + "\n");
|
|
chunkCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process any remaining data
|
|
if (buffer.trim()) {
|
|
process.stdout.write(buffer + "\n");
|
|
}
|
|
|
|
console.log(`\n\n✅ Received ${chunkCount} chunks`);
|
|
} else {
|
|
const responseData = await response.json();
|
|
console.log("\n📦 Response:");
|
|
console.log(JSON.stringify(responseData, null, 2));
|
|
}
|
|
} catch (err) {
|
|
console.error("\n❌ Request failed:", err.message);
|
|
if (process.env.DEBUG) {
|
|
console.error(err.stack);
|
|
}
|
|
process.exit(1);
|
|
}
|
|
})();
|