proekttarym-kayda/server.js

67 lines
3.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const http = require("http");
const fs = require("fs");
const path = require("path");
const PORT = process.env.PORT || 3000;
function body(req) {
return new Promise((resolve) => {
let s = "";
req.on("data", (c) => (s += c));
req.on("end", () => {
try { resolve(JSON.parse(s || "{}")); } catch (_) { resolve({}); }
});
});
}
async function ai(prompt) {
const r = await fetch(process.env.AI_BASE_URL + "/chat/completions", {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: "Bearer " + process.env.AI_API_KEY },
body: JSON.stringify({
model: process.env.AI_MODEL,
temperature: 0.4,
messages: [
{ role: "system", content: "Сен — артрит, артроз және анкилозды спондилит (Бехтерев ауруы) бар адамдарға арналған көмекші-кеңесші. Қазақ тілінде, жай сөзбен, қысқа әрі нақты жауап бер. Жауабыңда мыналар болсын: 1) шағымды қалай бағалауға болады; 2) қазір не істеу керек (жеңіл қозғалыс, жылу/суық, тыныштық) — жаттығуды тек ауырсынбай ғана орындау керектігін ескерт; 3) дәрі-дәрмекті тек дәрігер тағайындайтынын еске сал, дәрі атын ұсынба; 4) қандай белгілер кезінде дәрігерге шұғыл қаралу керектігін анық жаз (қатты күшейген ауырсыну, қатты ісіну, қызба, аяқ-қолды қозғалта алмау, сезімталдық жоғалуы). Шағымға байланысты пайдалы болса, осы бөлімдерге сілтеме ата: жаттығуларға — «Сайттағы Жаттығу бөлімінде дайын кешен бар» деп, дәрі/мазь/уколға — «Дәрілер және Аптека бөлімдерін қара» деп. Ақырында: «Бұл ақпарат дәрігер кеңесінің орнын баспайды» деп қос. Сен медициналық маман емессің." },
{ role: "user", content: prompt }
]
})
});
if (!r.ok) throw new Error("ИИ недоступен, код " + r.status);
const j = await r.json();
const out = j.choices?.[0]?.message?.content;
if (!out) throw new Error("ИИ вернул пустой ответ");
return out.trim();
}
http.createServer(async (req, res) => {
const url = req.url.split("?")[0];
if (req.method === "POST" && url === "/api/advice") {
const data = await body(req);
const text = (data.text || "").trim();
if (!text) {
res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
return res.end(JSON.stringify({ ok: false, error: "Хабарлама бос" }));
}
try {
const answer = await ai(text);
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
return res.end(JSON.stringify({ ok: true, answer }));
} catch (e) {
res.writeHead(502, { "Content-Type": "application/json; charset=utf-8" });
return res.end(JSON.stringify({ ok: false, error: "ИИ временно недоступен, попробуйте ещё раз" }));
}
}
let file = url === "/" ? "/index.html" : url;
const full = path.join(__dirname, file);
if (full.startsWith(__dirname) && fs.existsSync(full) && fs.statSync(full).isFile()) {
const ext = path.extname(full);
const type = ext === ".css" ? "text/css" : ext === ".js" ? "application/javascript" : "text/html";
res.writeHead(200, { "Content-Type": type + "; charset=utf-8" });
return res.end(fs.readFileSync(full));
}
res.writeHead(404); res.end("Not found");
}).listen(PORT, () => console.log("Сервер на порту " + PORT));