Spaces:
Running
Running
Update ai_engine.js
Browse files- ai_engine.js +53 -31
ai_engine.js
CHANGED
|
@@ -1,44 +1,61 @@
|
|
| 1 |
import OpenAI from "openai";
|
| 2 |
|
| 3 |
-
// Initialize
|
| 4 |
-
const
|
| 5 |
-
apiKey: process.env.
|
| 6 |
-
baseURL:
|
| 7 |
-
|
| 8 |
-
|
|
|
|
|
|
|
| 9 |
});
|
| 10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
// --- DEDICATED ERROR LOGGER ---
|
| 12 |
-
const
|
| 13 |
const status = error.status || error.response?.status;
|
| 14 |
|
| 15 |
-
if (status ===
|
| 16 |
-
console.error(`
|
|
|
|
|
|
|
| 17 |
} else if (status === 401) {
|
| 18 |
-
console.error(`🛑
|
| 19 |
} else if (status === 400) {
|
| 20 |
-
console.error(`⚠️ [
|
| 21 |
} else if (status >= 500) {
|
| 22 |
-
console.error(`🔥[
|
| 23 |
} else if (error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT') {
|
| 24 |
-
console.error(`⏳ [NETWORK TIMEOUT] ${context}: Connection to
|
| 25 |
} else {
|
| 26 |
-
console.error(`❌[
|
| 27 |
-
}
|
| 28 |
-
|
| 29 |
-
// Log the deep Azure inner error if it exists (crucial for content filter details)
|
| 30 |
-
if (error.error?.innererror) {
|
| 31 |
-
console.error(` ↳ Deep Error:`, JSON.stringify(error.error.innererror));
|
| 32 |
}
|
| 33 |
};
|
| 34 |
|
| 35 |
-
export const generateCompletion = async ({ prompt, system_prompt, images }) => {
|
| 36 |
try {
|
|
|
|
|
|
|
| 37 |
let messagesPayload =[
|
| 38 |
{ role: "system", content: system_prompt || "You are an elite AI assistant." }
|
| 39 |
];
|
| 40 |
|
| 41 |
-
// 🚨 VISION SUPPORT: OpenAI requires the data URI prefix for base64
|
| 42 |
if (images && Array.isArray(images) && images.length > 0) {
|
| 43 |
let userContent = [{ type: "text", text: prompt }];
|
| 44 |
|
|
@@ -54,20 +71,22 @@ export const generateCompletion = async ({ prompt, system_prompt, images }) => {
|
|
| 54 |
messagesPayload.push({ role: "user", content: prompt });
|
| 55 |
}
|
| 56 |
|
| 57 |
-
const response = await
|
| 58 |
-
model:
|
| 59 |
messages: messagesPayload,
|
| 60 |
-
//
|
|
|
|
| 61 |
});
|
| 62 |
|
| 63 |
return {
|
| 64 |
success: true,
|
| 65 |
data: response.choices[0].message.content,
|
| 66 |
-
usage: response.usage
|
|
|
|
| 67 |
};
|
| 68 |
|
| 69 |
} catch (error) {
|
| 70 |
-
|
| 71 |
return {
|
| 72 |
success: false,
|
| 73 |
error: error.message,
|
|
@@ -76,8 +95,10 @@ export const generateCompletion = async ({ prompt, system_prompt, images }) => {
|
|
| 76 |
}
|
| 77 |
};
|
| 78 |
|
| 79 |
-
export const streamCompletion = async ({ prompt, system_prompt, images, res }) => {
|
| 80 |
try {
|
|
|
|
|
|
|
| 81 |
let messagesPayload =[
|
| 82 |
{ role: "system", content: system_prompt || "You are an elite AI assistant." }
|
| 83 |
];
|
|
@@ -97,8 +118,8 @@ export const streamCompletion = async ({ prompt, system_prompt, images, res }) =
|
|
| 97 |
messagesPayload.push({ role: "user", content: prompt });
|
| 98 |
}
|
| 99 |
|
| 100 |
-
const stream = await
|
| 101 |
-
model:
|
| 102 |
messages: messagesPayload,
|
| 103 |
stream: true,
|
| 104 |
stream_options: { include_usage: true }
|
|
@@ -109,6 +130,7 @@ export const streamCompletion = async ({ prompt, system_prompt, images, res }) =
|
|
| 109 |
for await (const chunk of stream) {
|
| 110 |
const delta = chunk.choices[0]?.delta;
|
| 111 |
|
|
|
|
| 112 |
if (delta?.reasoning_content) {
|
| 113 |
res.write(`__THINK__${delta.reasoning_content}`);
|
| 114 |
} else if (delta?.content) {
|
|
@@ -124,9 +146,9 @@ export const streamCompletion = async ({ prompt, system_prompt, images, res }) =
|
|
| 124 |
res.end();
|
| 125 |
|
| 126 |
} catch (error) {
|
| 127 |
-
|
| 128 |
|
| 129 |
-
// Safely write the error to the stream so the frontend doesn't hang forever
|
| 130 |
res.write(`\n\n[ERROR]: ${error.message}`);
|
| 131 |
res.end();
|
| 132 |
}
|
|
|
|
| 1 |
import OpenAI from "openai";
|
| 2 |
|
| 3 |
+
// Initialize OpenRouter using the standard OpenAI SDK
|
| 4 |
+
const openRouterAI = new OpenAI({
|
| 5 |
+
apiKey: process.env.OPENROUTER_API_KEY,
|
| 6 |
+
baseURL: "https://openrouter.ai/api/v1",
|
| 7 |
+
defaultHeaders: {
|
| 8 |
+
"HTTP-Referer": process.env.YOUR_SITE_URL || "https://everydaycats.web.apl", // Optional: Helps with OpenRouter rankings
|
| 9 |
+
"X-Title": process.env.YOUR_SITE_NAME || "Aura Scanner", // Optional: Shows in OpenRouter dashboard
|
| 10 |
+
}
|
| 11 |
});
|
| 12 |
|
| 13 |
+
// --- DYNAMIC MODEL ROUTER ---
|
| 14 |
+
const getModelId = (modelName) => {
|
| 15 |
+
switch(modelName?.toLowerCase()) {
|
| 16 |
+
case "llama":
|
| 17 |
+
case "maverick":
|
| 18 |
+
return "meta-llama/llama-4-maverick";
|
| 19 |
+
case "gpt":
|
| 20 |
+
case "gpt-5.4-nano":
|
| 21 |
+
return "openai/gpt-5.4-nano";
|
| 22 |
+
case "qwen":
|
| 23 |
+
default:
|
| 24 |
+
// The Default Winner: Cheapest input, massive throughput, perfect for JSON & Lore
|
| 25 |
+
return "qwen/qwen3-vl-30b-a3b-thinking";
|
| 26 |
+
}
|
| 27 |
+
};
|
| 28 |
+
|
| 29 |
// --- DEDICATED ERROR LOGGER ---
|
| 30 |
+
const logOpenRouterError = (error, context) => {
|
| 31 |
const status = error.status || error.response?.status;
|
| 32 |
|
| 33 |
+
if (status === 402) {
|
| 34 |
+
console.error(`💸 [OPENROUTER FUNDS - 402] ${context}: Insufficient credits! Top up your OpenRouter account.`);
|
| 35 |
+
} else if (status === 429) {
|
| 36 |
+
console.error(`🚨[OPENROUTER THROTTLING - 429] ${context}: Rate limit exceeded. Provider is congested.`);
|
| 37 |
} else if (status === 401) {
|
| 38 |
+
console.error(`🛑[OPENROUTER AUTH - 401] ${context}: Invalid API Key.`);
|
| 39 |
} else if (status === 400) {
|
| 40 |
+
console.error(`⚠️ [OPENROUTER BAD REQUEST - 400] ${context}: Invalid payload, image format, or context length exceeded.`);
|
| 41 |
} else if (status >= 500) {
|
| 42 |
+
console.error(`🔥[OPENROUTER SERVER ERROR - ${status}] ${context}: The specific AI provider backend is down.`);
|
| 43 |
} else if (error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT') {
|
| 44 |
+
console.error(`⏳ [NETWORK TIMEOUT] ${context}: Connection to OpenRouter dropped.`);
|
| 45 |
} else {
|
| 46 |
+
console.error(`❌[OPENROUTER ERROR] ${context}:`, error.message || error);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
}
|
| 48 |
};
|
| 49 |
|
| 50 |
+
export const generateCompletion = async ({ model, prompt, system_prompt, images }) => {
|
| 51 |
try {
|
| 52 |
+
const targetModel = getModelId(model);
|
| 53 |
+
|
| 54 |
let messagesPayload =[
|
| 55 |
{ role: "system", content: system_prompt || "You are an elite AI assistant." }
|
| 56 |
];
|
| 57 |
|
| 58 |
+
// 🚨 VISION SUPPORT: OpenAI/OpenRouter requires the data URI prefix for base64
|
| 59 |
if (images && Array.isArray(images) && images.length > 0) {
|
| 60 |
let userContent = [{ type: "text", text: prompt }];
|
| 61 |
|
|
|
|
| 71 |
messagesPayload.push({ role: "user", content: prompt });
|
| 72 |
}
|
| 73 |
|
| 74 |
+
const response = await openRouterAI.chat.completions.create({
|
| 75 |
+
model: targetModel,
|
| 76 |
messages: messagesPayload,
|
| 77 |
+
// Force JSON output (Supported by Qwen, Llama, and GPT on OpenRouter)
|
| 78 |
+
response_format: { type: "json_object" }
|
| 79 |
});
|
| 80 |
|
| 81 |
return {
|
| 82 |
success: true,
|
| 83 |
data: response.choices[0].message.content,
|
| 84 |
+
usage: response.usage,
|
| 85 |
+
model_used: targetModel
|
| 86 |
};
|
| 87 |
|
| 88 |
} catch (error) {
|
| 89 |
+
logOpenRouterError(error, "generateCompletion");
|
| 90 |
return {
|
| 91 |
success: false,
|
| 92 |
error: error.message,
|
|
|
|
| 95 |
}
|
| 96 |
};
|
| 97 |
|
| 98 |
+
export const streamCompletion = async ({ model, prompt, system_prompt, images, res }) => {
|
| 99 |
try {
|
| 100 |
+
const targetModel = getModelId(model);
|
| 101 |
+
|
| 102 |
let messagesPayload =[
|
| 103 |
{ role: "system", content: system_prompt || "You are an elite AI assistant." }
|
| 104 |
];
|
|
|
|
| 118 |
messagesPayload.push({ role: "user", content: prompt });
|
| 119 |
}
|
| 120 |
|
| 121 |
+
const stream = await openRouterAI.chat.completions.create({
|
| 122 |
+
model: targetModel,
|
| 123 |
messages: messagesPayload,
|
| 124 |
stream: true,
|
| 125 |
stream_options: { include_usage: true }
|
|
|
|
| 130 |
for await (const chunk of stream) {
|
| 131 |
const delta = chunk.choices[0]?.delta;
|
| 132 |
|
| 133 |
+
// Handle Qwen's specific "Thinking" blocks if they stream them
|
| 134 |
if (delta?.reasoning_content) {
|
| 135 |
res.write(`__THINK__${delta.reasoning_content}`);
|
| 136 |
} else if (delta?.content) {
|
|
|
|
| 146 |
res.end();
|
| 147 |
|
| 148 |
} catch (error) {
|
| 149 |
+
logOpenRouterError(error, "streamCompletion");
|
| 150 |
|
| 151 |
+
// Safely write the error to the stream so the frontend doesn't hang forever
|
| 152 |
res.write(`\n\n[ERROR]: ${error.message}`);
|
| 153 |
res.end();
|
| 154 |
}
|