everydaycats commited on
Commit
64d44b4
·
verified ·
1 Parent(s): bc46703

Update ai_engine.js

Browse files
Files changed (1) hide show
  1. ai_engine.js +53 -31
ai_engine.js CHANGED
@@ -1,44 +1,61 @@
1
  import OpenAI from "openai";
2
 
3
- // Initialize Azure OpenAI strictly from environment variables
4
- const azureOpenAI = new OpenAI({
5
- apiKey: process.env.AZURE_OPENAI_API_KEY,
6
- baseURL: `${process.env.AZURE_OPENAI_ENDPOINT}/openai/deployments/${process.env.AZURE_OPENAI_DEPLOYMENT_NAME}`,
7
- defaultQuery: { "api-version": process.env.AZURE_OPENAI_API_VERSION || "2024-05-01-preview" },
8
- defaultHeaders: { "api-key": process.env.AZURE_OPENAI_API_KEY }
 
 
9
  });
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  // --- DEDICATED ERROR LOGGER ---
12
- const logAzureError = (error, context) => {
13
  const status = error.status || error.response?.status;
14
 
15
- if (status === 429) {
16
- console.error(`🚨 [AZURE THROTTLING - 429] ${context}: Rate limit exceeded or quota exhausted. Slow down requests.`);
 
 
17
  } else if (status === 401) {
18
- console.error(`🛑 [AZURE AUTH - 401] ${context}: Invalid API Key or Endpoint URL.`);
19
  } else if (status === 400) {
20
- console.error(`⚠️ [AZURE BAD REQUEST - 400] ${context}: Invalid payload, image too large, or Azure Content Filter triggered.`);
21
  } else if (status >= 500) {
22
- console.error(`🔥[AZURE SERVER ERROR - ${status}] ${context}: Azure backend is struggling or down.`);
23
  } else if (error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT') {
24
- console.error(`⏳ [NETWORK TIMEOUT] ${context}: Connection to Azure dropped.`);
25
  } else {
26
- console.error(`❌[AZURE ERROR] ${context}:`, error.message || 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 azureOpenAI.chat.completions.create({
58
- model: process.env.AZURE_OPENAI_DEPLOYMENT_NAME,
59
  messages: messagesPayload,
60
- // reasoning_effort: "high" // Uncomment if you want to force deeper reasoning
 
61
  });
62
 
63
  return {
64
  success: true,
65
  data: response.choices[0].message.content,
66
- usage: response.usage
 
67
  };
68
 
69
  } catch (error) {
70
- logAzureError(error, "generateCompletion");
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 azureOpenAI.chat.completions.create({
101
- model: process.env.AZURE_OPENAI_DEPLOYMENT_NAME,
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
- logAzureError(error, "streamCompletion");
128
 
129
- // Safely write the error to the stream so the frontend doesn't hang forever waiting for chunks
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
  }