Update server.js
Browse files
server.js
CHANGED
|
@@ -4,106 +4,166 @@ import { fileURLToPath } from "url";
|
|
| 4 |
import dotenv from "dotenv";
|
| 5 |
import bodyParser from "body-parser";
|
| 6 |
|
|
|
|
|
|
|
|
|
|
| 7 |
dotenv.config();
|
| 8 |
|
| 9 |
const app = express();
|
|
|
|
| 10 |
const __filename = fileURLToPath(import.meta.url);
|
| 11 |
const __dirname = path.dirname(__filename);
|
| 12 |
-
|
| 13 |
-
const
|
| 14 |
|
| 15 |
app.use(bodyParser.json());
|
| 16 |
app.use(express.static(path.join(__dirname, "dist")));
|
| 17 |
|
| 18 |
app.post("/api/ask-ai", async (req, res) => {
|
| 19 |
-
const { prompt, html, previousPrompt } = req.body;
|
| 20 |
if (!prompt) {
|
| 21 |
-
return res.status(400).send({
|
| 22 |
-
}
|
| 23 |
-
|
| 24 |
-
if (!OPENROUTER_API_KEY) {
|
| 25 |
-
return res.status(500).send({
|
| 26 |
ok: false,
|
| 27 |
-
message: "Missing
|
| 28 |
});
|
| 29 |
}
|
| 30 |
|
|
|
|
| 31 |
res.setHeader("Content-Type", "text/plain");
|
| 32 |
res.setHeader("Cache-Control", "no-cache");
|
| 33 |
res.setHeader("Connection", "keep-alive");
|
| 34 |
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
"HTTP-Referer": "https://raypages.com", // personalize aqui
|
| 53 |
-
"X-Title": "RayPages AI",
|
| 54 |
-
},
|
| 55 |
-
body: JSON.stringify({
|
| 56 |
-
model: "deepseek-chat-v3.0",
|
| 57 |
-
stream: true,
|
| 58 |
-
messages,
|
| 59 |
-
}),
|
| 60 |
});
|
|
|
|
| 61 |
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
});
|
| 67 |
-
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
let completeResponse = "";
|
| 72 |
|
| 73 |
-
const stream = async () => {
|
| 74 |
while (true) {
|
| 75 |
const { done, value } = await reader.read();
|
| 76 |
if (done) break;
|
| 77 |
|
| 78 |
-
const chunk = decoder.decode(value
|
| 79 |
-
const
|
| 80 |
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
const text = match
|
| 84 |
-
.replace(/"content":"|"/g, "")
|
| 85 |
-
.replace(/\\n/g, "\n")
|
| 86 |
-
.replace(/\\t/g, "\t");
|
| 87 |
|
| 88 |
-
|
| 89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
|
| 91 |
-
if (
|
| 92 |
-
|
| 93 |
-
return;
|
| 94 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
}
|
| 96 |
}
|
|
|
|
| 97 |
}
|
| 98 |
res.end();
|
| 99 |
-
}
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
}
|
| 108 |
});
|
| 109 |
|
|
@@ -112,5 +172,5 @@ app.get("*", (_req, res) => {
|
|
| 112 |
});
|
| 113 |
|
| 114 |
app.listen(PORT, () => {
|
| 115 |
-
console.log(
|
| 116 |
-
});
|
|
|
|
| 4 |
import dotenv from "dotenv";
|
| 5 |
import bodyParser from "body-parser";
|
| 6 |
|
| 7 |
+
import { PROVIDERS } from "./utils/providers.js";
|
| 8 |
+
|
| 9 |
+
// Load environment variables from .env file
|
| 10 |
dotenv.config();
|
| 11 |
|
| 12 |
const app = express();
|
| 13 |
+
|
| 14 |
const __filename = fileURLToPath(import.meta.url);
|
| 15 |
const __dirname = path.dirname(__filename);
|
| 16 |
+
|
| 17 |
+
const PORT = process.env.APP_PORT || 3000;
|
| 18 |
|
| 19 |
app.use(bodyParser.json());
|
| 20 |
app.use(express.static(path.join(__dirname, "dist")));
|
| 21 |
|
| 22 |
app.post("/api/ask-ai", async (req, res) => {
|
| 23 |
+
const { prompt, html, previousPrompt, provider } = req.body;
|
| 24 |
if (!prompt) {
|
| 25 |
+
return res.status(400).send({
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
ok: false,
|
| 27 |
+
message: "Missing required fields",
|
| 28 |
});
|
| 29 |
}
|
| 30 |
|
| 31 |
+
// Set up response headers for streaming
|
| 32 |
res.setHeader("Content-Type", "text/plain");
|
| 33 |
res.setHeader("Cache-Control", "no-cache");
|
| 34 |
res.setHeader("Connection", "keep-alive");
|
| 35 |
|
| 36 |
+
let systemPrompt = `ONLY USE HTML, CSS AND JAVASCRIPT. No explanations, ONLY CODE. If you want to use ICON make sure to import the library first. Try to create the best UI possible by using only HTML, CSS and JAVASCRIPT. Use as much as you can TailwindCSS for the CSS, if you can't do something with TailwindCSS, then use custom CSS (make sure to import <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script> in the head). Also, try to ellaborate as much as you can, to create something unique. ALWAYS GIVE THE RESPONSE INTO A SINGLE HTML FILE`;
|
| 37 |
+
|
| 38 |
+
let TOKENS_USED = prompt?.length;
|
| 39 |
+
if (previousPrompt) TOKENS_USED += previousPrompt.length;
|
| 40 |
+
if (html) TOKENS_USED += html.length;
|
| 41 |
+
|
| 42 |
+
const DEFAULT_PROVIDER = PROVIDERS.openrouter;
|
| 43 |
+
const selectedProvider =
|
| 44 |
+
provider === "auto"
|
| 45 |
+
? DEFAULT_PROVIDER
|
| 46 |
+
: PROVIDERS[provider] ?? DEFAULT_PROVIDER;
|
| 47 |
+
|
| 48 |
+
if (provider !== "auto" && TOKENS_USED >= selectedProvider.max_tokens) {
|
| 49 |
+
return res.status(400).send({
|
| 50 |
+
ok: false,
|
| 51 |
+
openSelectProvider: true,
|
| 52 |
+
message: `Context is too long. ${selectedProvider.name} allow ${selectedProvider.max_tokens} max tokens.`,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
});
|
| 54 |
+
}
|
| 55 |
|
| 56 |
+
if (["local", "openrouter"].includes(selectedProvider.id)) {
|
| 57 |
+
try {
|
| 58 |
+
const { ApiKey, ApiUrl, Model } = req.body;
|
| 59 |
+
if (!ApiUrl || !Model) {
|
| 60 |
+
return res.status(400).send({
|
| 61 |
+
ok: false,
|
| 62 |
+
message: "Missing required fields for provider, set API KEY, BASE URL, and MODEL.",
|
| 63 |
+
});
|
| 64 |
+
}
|
| 65 |
+
const response = await fetch(`${ApiUrl}/chat/completions`, {
|
| 66 |
+
method: "POST",
|
| 67 |
+
headers: {
|
| 68 |
+
"Content-Type": "application/json",
|
| 69 |
+
Authorization: `Bearer ${ApiKey}`,
|
| 70 |
+
},
|
| 71 |
+
body: JSON.stringify({
|
| 72 |
+
model: Model,
|
| 73 |
+
messages: [
|
| 74 |
+
{
|
| 75 |
+
role: "system",
|
| 76 |
+
content: systemPrompt,
|
| 77 |
+
},
|
| 78 |
+
...(previousPrompt
|
| 79 |
+
? [
|
| 80 |
+
{
|
| 81 |
+
role: "user",
|
| 82 |
+
content: previousPrompt,
|
| 83 |
+
},
|
| 84 |
+
]
|
| 85 |
+
: []),
|
| 86 |
+
...(html
|
| 87 |
+
? [
|
| 88 |
+
{
|
| 89 |
+
role: "assistant",
|
| 90 |
+
content: `The current code is: ${html}.`,
|
| 91 |
+
},
|
| 92 |
+
]
|
| 93 |
+
: []),
|
| 94 |
+
{
|
| 95 |
+
role: "user",
|
| 96 |
+
content: prompt,
|
| 97 |
+
},
|
| 98 |
+
],
|
| 99 |
+
stream: true
|
| 100 |
+
})
|
| 101 |
});
|
| 102 |
+
if (!response.ok) {
|
| 103 |
+
const errorBody = await response.text();
|
| 104 |
+
throw new Error(`API Error: ${response.status} - ${errorBody}`);
|
| 105 |
+
}
|
| 106 |
|
| 107 |
+
const reader = response.body.getReader();
|
| 108 |
+
const decoder = new TextDecoder();
|
|
|
|
| 109 |
|
|
|
|
| 110 |
while (true) {
|
| 111 |
const { done, value } = await reader.read();
|
| 112 |
if (done) break;
|
| 113 |
|
| 114 |
+
const chunk = decoder.decode(value);
|
| 115 |
+
const lines = chunk.split('\n').filter(line => line.trim());
|
| 116 |
|
| 117 |
+
for (const line of lines) {
|
| 118 |
+
if (!line || !line.startsWith('data: ') || line.includes('[DONE]')) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
|
| 120 |
+
try {
|
| 121 |
+
if (line.includes('exceeded')) {
|
| 122 |
+
return res.status(402).send({
|
| 123 |
+
ok: false,
|
| 124 |
+
message: line,
|
| 125 |
+
});
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
const json = line.slice(6).trim();
|
| 129 |
|
| 130 |
+
if (!json.startsWith('{')) {
|
| 131 |
+
continue;
|
|
|
|
| 132 |
}
|
| 133 |
+
|
| 134 |
+
const message = JSON.parse(json);
|
| 135 |
+
const content = message?.choices?.[0]?.delta?.content;
|
| 136 |
+
|
| 137 |
+
if (content) {
|
| 138 |
+
res.write(content);
|
| 139 |
+
}
|
| 140 |
+
} catch (e) {
|
| 141 |
+
console.error('Error on line stream:', e.message);
|
| 142 |
+
// continue ao invés de throw, pra não matar tudo por causa de uma linha podre
|
| 143 |
+
continue;
|
| 144 |
}
|
| 145 |
}
|
| 146 |
+
|
| 147 |
}
|
| 148 |
res.end();
|
| 149 |
+
} catch (error) {
|
| 150 |
+
if (error.message.includes("exceeded")) {
|
| 151 |
+
return res.status(402).send({
|
| 152 |
+
ok: false,
|
| 153 |
+
message: error.message,
|
| 154 |
+
});
|
| 155 |
+
}
|
| 156 |
+
if (!res.headersSent) {
|
| 157 |
+
res.status(500).send({
|
| 158 |
+
ok: false,
|
| 159 |
+
message:
|
| 160 |
+
error.message || "An error occurred while processing your request.",
|
| 161 |
+
});
|
| 162 |
+
} else {
|
| 163 |
+
// Otherwise end the stream
|
| 164 |
+
res.end();
|
| 165 |
+
}
|
| 166 |
+
}
|
| 167 |
}
|
| 168 |
});
|
| 169 |
|
|
|
|
| 172 |
});
|
| 173 |
|
| 174 |
app.listen(PORT, () => {
|
| 175 |
+
console.log(`Server is running on port ${PORT}`);
|
| 176 |
+
});
|