everydaycats commited on
Commit
cdc5389
·
verified ·
1 Parent(s): 2ff417d

Create aiEngine.js

Browse files
Files changed (1) hide show
  1. aiEngine.js +169 -0
aiEngine.js ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import OpenAI from "openai";
2
+
3
+ const openRouterAI = new OpenAI({
4
+ apiKey: process.env.OPENROUTER_API_KEY,
5
+ baseURL: "https://openrouter.ai/api/v1",
6
+ defaultHeaders: {
7
+ "HTTP-Referer": process.env.YOUR_SITE_URL || "https://everydaycats.web.app",
8
+ "X-Title": process.env.YOUR_SITE_NAME || "Aura Scanner",
9
+ }
10
+ });
11
+
12
+ // --- MODEL ROUTER ---
13
+ const getModelId = (modelName) => {
14
+ switch (modelName?.toLowerCase()) {
15
+ case "llama": case "maverick": return "meta-llama/llama-4-maverick:floor";
16
+ case "llama-3.2-11b": return "meta-llama/llama-3.2-11b-vision-instruct:free";
17
+ case "qwen-2-vl-7b": return "qwen/qwen-2-vl-7b-instruct:free";
18
+ case "nemotron-12b": return "nvidia/nemotron-nano-12b-v2-vl:free";
19
+ case "gpt": case "gpt-5.4-nano": return "openai/gpt-5.4-nano";
20
+ case "qwen-non": return "qwen/qwen3-vl-30b-a3b-instruct";
21
+ case "qwen": default: return "qwen/qwen3-vl-30b-a3b-thinking:floor";
22
+ }
23
+ };
24
+
25
+ // --- IMAGE MODEL ROUTER ---
26
+ const getImageModelId = (tier) => {
27
+ switch (tier) {
28
+ case "premium": return "bytedance-seed/seedream-4.5"; // $0.04/img
29
+ case "quick": return "sourceful/riverflow-v2-fast"; // $0.002/img
30
+ default: return "sourceful/riverflow-v2-fast";
31
+ }
32
+ };
33
+
34
+ // --- ERROR LOGGER ---
35
+ const logOpenRouterError = (error, context) => {
36
+ const status = error.status || error.response?.status;
37
+ if (status === 402) console.error(`💸 [402] ${context}: Insufficient credits.`);
38
+ else if (status === 429) console.error(`🚨 [429] ${context}: Rate limit exceeded.`);
39
+ else if (status === 401) console.error(`🛑 [401] ${context}: Invalid API Key.`);
40
+ else if (status === 400) console.error(`⚠️ [400] ${context}: Bad request / invalid payload.`);
41
+ else if (status >= 500) console.error(`🔥 [${status}] ${context}: Provider backend down.`);
42
+ else if (error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT')
43
+ console.error(`⏳ [TIMEOUT] ${context}: Connection dropped.`);
44
+ else console.error(`❌ [ERROR] ${context}:`, error.message || error);
45
+ };
46
+
47
+ // --- TEXT COMPLETION ---
48
+ export const generateCompletion = async ({ model, prompt, system_prompt, images }) => {
49
+ try {
50
+ const targetModel = getModelId(model);
51
+ let messagesPayload = [
52
+ { role: "system", content: system_prompt || "You are an elite AI assistant." }
53
+ ];
54
+
55
+ if (images?.length > 0) {
56
+ let userContent = [{ type: "text", text: prompt }];
57
+ images.forEach((imgStr) => {
58
+ if (imgStr.length > 7000000) throw new Error("Image too large. Max ~4MB.");
59
+ const formattedImg = imgStr.startsWith('data:image')
60
+ ? imgStr : `data:image/jpeg;base64,${imgStr}`;
61
+ userContent.push({ type: "image_url", image_url: { url: formattedImg } });
62
+ });
63
+ messagesPayload.push({ role: "user", content: userContent });
64
+ } else {
65
+ messagesPayload.push({ role: "user", content: prompt });
66
+ }
67
+
68
+ const response = await openRouterAI.chat.completions.create({
69
+ model: targetModel,
70
+ messages: messagesPayload,
71
+ response_format: { type: "json_object" },
72
+ max_tokens: 1200,
73
+ });
74
+
75
+ return {
76
+ success: true,
77
+ data: response.choices[0].message.content,
78
+ usage: response.usage,
79
+ model_used: targetModel
80
+ };
81
+ } catch (error) {
82
+ logOpenRouterError(error, "generateCompletion");
83
+ return { success: false, error: error.message, status: error.status || 500 };
84
+ }
85
+ };
86
+
87
+ // --- STREAMING TEXT COMPLETION ---
88
+ export const streamCompletion = async ({ model, prompt, system_prompt, images, res }) => {
89
+ try {
90
+ const targetModel = getModelId(model);
91
+ let messagesPayload = [
92
+ { role: "system", content: system_prompt || "You are an elite AI assistant." }
93
+ ];
94
+
95
+ if (images?.length > 0) {
96
+ let userContent = [{ type: "text", text: prompt }];
97
+ images.forEach((imgStr) => {
98
+ if (imgStr.length > 7000000) throw new Error("Image too large. Max ~4MB.");
99
+ const formattedImg = imgStr.startsWith('data:image')
100
+ ? imgStr : `data:image/jpeg;base64,${imgStr}`;
101
+ userContent.push({ type: "image_url", image_url: { url: formattedImg } });
102
+ });
103
+ messagesPayload.push({ role: "user", content: userContent });
104
+ } else {
105
+ messagesPayload.push({ role: "user", content: prompt });
106
+ }
107
+
108
+ const stream = await openRouterAI.chat.completions.create({
109
+ model: targetModel,
110
+ messages: messagesPayload,
111
+ stream: true,
112
+ stream_options: { include_usage: true },
113
+ max_tokens: 2000,
114
+ });
115
+
116
+ let totalTokenCount = 0;
117
+ for await (const chunk of stream) {
118
+ const delta = chunk.choices[0]?.delta;
119
+ if (delta?.reasoning_content) res.write(`__THINK__${delta.reasoning_content}`);
120
+ else if (delta?.content) res.write(delta.content);
121
+ if (chunk.usage) totalTokenCount = chunk.usage.total_tokens;
122
+ }
123
+
124
+ res.write(`__USAGE__${JSON.stringify({ totalTokenCount })}`);
125
+ res.end();
126
+ } catch (error) {
127
+ logOpenRouterError(error, "streamCompletion");
128
+ res.write(`\n\n[ERROR]: ${error.message}`);
129
+ res.end();
130
+ }
131
+ };
132
+
133
+ // --- IMAGE GENERATION ---
134
+ // Uses OpenRouter's image generation endpoint directly (not chat completions)
135
+ export const generateImage = async ({ prompt, tier = "quick" }) => {
136
+ try {
137
+ const model = getImageModelId(tier);
138
+
139
+ const response = await fetch("https://openrouter.ai/api/v1/images/generations", {
140
+ method: "POST",
141
+ headers: {
142
+ "Authorization": `Bearer ${process.env.OPENROUTER_API_KEY}`,
143
+ "Content-Type": "application/json",
144
+ "HTTP-Referer": process.env.YOUR_SITE_URL || "https://everydaycats.web.app",
145
+ "X-Title": process.env.YOUR_SITE_NAME || "lightnovel.ai",
146
+ },
147
+ body: JSON.stringify({
148
+ model,
149
+ prompt,
150
+ n: 1,
151
+ size: "1024x1024",
152
+ })
153
+ });
154
+
155
+ if (!response.ok) {
156
+ const err = await response.json();
157
+ throw Object.assign(new Error(err?.error?.message || "Image gen failed"), { status: response.status });
158
+ }
159
+
160
+ const data = await response.json();
161
+ const imageUrl = data?.data?.[0]?.url;
162
+ if (!imageUrl) throw new Error("No image URL returned.");
163
+
164
+ return { success: true, url: imageUrl, model_used: model };
165
+ } catch (error) {
166
+ logOpenRouterError(error, "generateImage");
167
+ return { success: false, error: error.message, status: error.status || 500 };
168
+ }
169
+ };