ai_service.mjs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import path from 'path';
  2. import fs from 'fs';
  3. import { fileURLToPath } from 'url';
  4. import express from '../server/node_modules/express/index.js';
  5. import cors from '../server/node_modules/cors/lib/index.js';
  6. import { getLlama, LlamaChatSession, LlamaCompletion } from '../server/node_modules/node-llama-cpp/dist/index.js';
  7. let getConfig, buildAIContext;
  8. try {
  9. getConfig = (await import('../configs/config-manager.js')).getConfig;
  10. } catch {}
  11. try {
  12. const mod = await import('./buildAIContext.js');
  13. buildAIContext = mod.default || mod.buildContext;
  14. } catch {}
  15. const app = express();
  16. app.use(cors());
  17. app.use(express.json());
  18. const __filename = fileURLToPath(import.meta.url);
  19. const __dirname = path.dirname(__filename);
  20. let llamaInstance, model, context, session;
  21. let rawContext, rawCompletion;
  22. let ready = false;
  23. let lastError = null;
  24. async function initModel() {
  25. if (model) return;
  26. const modelPath = path.join(__dirname, 'oasis-42-1-chat.Q4_K_M.gguf');
  27. if (!fs.existsSync(modelPath)) {
  28. throw new Error(`Model file not found at: ${modelPath}`);
  29. }
  30. llamaInstance = await getLlama({ gpu: false });
  31. model = await llamaInstance.loadModel({ modelPath });
  32. context = await model.createContext();
  33. session = new LlamaChatSession({ contextSequence: context.getSequence() });
  34. ready = true;
  35. }
  36. async function initRaw() {
  37. if (rawCompletion) return;
  38. if (!model) await initModel();
  39. rawContext = await model.createContext();
  40. rawCompletion = new LlamaCompletion({ contextSequence: rawContext.getSequence() });
  41. }
  42. app.post('/ai', async (req, res) => {
  43. try {
  44. const sanitize = (s) => String(s || '').replace(/[<>"'`]/g, '').replace(/\b(ignore|disregard|forget|system|instruction|prompt)\b/gi, '[$1]').trim();
  45. const userInput = sanitize(String(req.body.input || ''));
  46. if (req.body.raw === true) {
  47. await initRaw();
  48. const answer = await rawCompletion.generateCompletion(userInput, { maxTokens: 120 });
  49. return res.json({ answer: String(answer || '').trim(), snippets: [] });
  50. }
  51. await initModel();
  52. let userContext = '';
  53. let snippets = [];
  54. try {
  55. userContext = await (buildAIContext ? buildAIContext(120) : '');
  56. if (userContext) {
  57. userContext = userContext.split('\n').map(l => sanitize(l)).join('\n');
  58. snippets = userContext.split('\n').slice(0, 50);
  59. }
  60. } catch {}
  61. const config = getConfig?.() || {};
  62. const baseContext = 'Context: You are an AI assistant called "42" in Oasis, a distributed, encrypted and federated social network.';
  63. const userPrompt = [baseContext, config.ai?.prompt?.trim() || 'Provide an informative and precise response.'].join('\n');
  64. const prompt = [
  65. userPrompt,
  66. userContext ? `--- USER DATA START ---\n${userContext}\n--- USER DATA END ---` : '',
  67. `--- QUERY START ---\n${userInput}\n--- QUERY END ---`
  68. ].filter(Boolean).join('\n\n');
  69. const answer = await session.prompt(prompt);
  70. res.json({ answer: String(answer || '').trim(), snippets });
  71. } catch {}
  72. });
  73. app.post('/ai/train', async (req, res) => {
  74. res.json({ stored: true });
  75. });
  76. app.listen(4001);