Emalawi19 commited on
Commit
8504fa4
Β·
verified Β·
1 Parent(s): 8bd81cb

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +39 -37
server.js CHANGED
@@ -9,7 +9,7 @@ const KNOWLEDGE_DIR = './knowledge';
9
  let generator;
10
  let knowledgeBase = [];
11
 
12
- // ── LOAD KNOWLEDGE FILES ──────────────────────────────────────────────────────
13
  function loadKnowledge() {
14
  if (!fs.existsSync(KNOWLEDGE_DIR)) { fs.mkdirSync(KNOWLEDGE_DIR); return; }
15
  const files = fs.readdirSync(KNOWLEDGE_DIR).filter(f => f.endsWith('.txt'));
@@ -19,7 +19,7 @@ function loadKnowledge() {
19
  const chunks = splitChunks(content, 600, 60);
20
  chunks.forEach(c => knowledgeBase.push({ source: file, text: c }));
21
  }
22
- console.log(`Knowledge: ${files.length} files, ${knowledgeBase.length} chunks`);
23
  }
24
 
25
  function splitChunks(text, size, overlap) {
@@ -47,7 +47,7 @@ function retrieveContext(prompt, topK = 4) {
47
  .join('\n\n---\n\n');
48
  }
49
 
50
- // ── LOAD MODEL ────────────────────────────────────────────────────────────────
51
  async function loadModel() {
52
  console.log("Loading model...");
53
  generator = await pipeline('text-generation', MODEL_NAME, { dtype: 'q4' });
@@ -61,7 +61,12 @@ async function generateResponse(messages) {
61
  repetition_penalty: 1.2,
62
  do_sample: false
63
  });
64
- return output[0].generated_text.at(-1).content || '';
 
 
 
 
 
65
  }
66
 
67
  // ── SERVER ────────────────────────────────────────────────────────────────────
@@ -74,12 +79,18 @@ const server = http.createServer(async (req, res) => {
74
 
75
  const pathname = req.url.split('?')[0];
76
 
 
77
  if (pathname === '/' && req.method === 'GET') {
78
  res.setHeader('Content-Type', 'application/json');
79
  res.writeHead(200);
80
- return res.end(JSON.stringify({ status: "running", model: MODEL_NAME, knowledge_chunks: knowledgeBase.length }));
 
 
 
 
81
  }
82
 
 
83
  if (pathname === '/reload-knowledge' && req.method === 'POST') {
84
  loadKnowledge();
85
  res.setHeader('Content-Type', 'application/json');
@@ -87,66 +98,57 @@ const server = http.createServer(async (req, res) => {
87
  return res.end(JSON.stringify({ message: `Reloaded: ${knowledgeBase.length} chunks` }));
88
  }
89
 
 
90
  if (pathname === '/generate' && req.method === 'POST') {
91
  let body = '';
92
  req.on('data', c => { body += c.toString(); });
93
  req.on('end', async () => {
 
 
94
  try {
95
  const { prompt, system } = JSON.parse(body);
 
96
  if (!generator) {
97
- res.setHeader('Content-Type', 'application/json');
98
  res.writeHead(503);
99
- return res.end(JSON.stringify({ error: "Model loading..." }));
100
  }
101
 
102
- // Retrieve from knowledge files
103
  const ragContext = retrieveContext(prompt, 4);
104
  const ragSection = ragContext
105
- ? `\n\nKNOWLEDGE BASE (use this for your answer):\n${ragContext}\n`
106
  : '';
107
 
108
- // Use system prompt from frontend if provided, else default
109
- const finalSystem = system
110
- ? system + ragSection
111
- : `You are Mlimi Connect AI, a free agricultural advisor for Malawian farmers. Only answer agriculture questions.${ragSection}`;
112
 
113
  const messages = [
114
  { role: 'system', content: finalSystem },
115
  { role: 'user', content: prompt }
116
  ];
117
 
118
- // SSE streaming
119
- res.writeHead(200, {
120
- 'Content-Type': 'text/event-stream',
121
- 'Cache-Control': 'no-cache',
122
- 'Connection': 'keep-alive',
123
- 'X-Accel-Buffering': 'no'
124
- });
125
-
126
- res.write(`data: ${JSON.stringify({ status: "generating" })}\n\n`);
127
 
128
  let result = await generateResponse(messages);
129
 
130
- if (!result || result.trim().length < 3) {
131
- result = ragContext
132
- ? `Based on available knowledge:\n\n${ragContext.slice(0, 400)}`
133
- : "I don't have specific information on that. Please consult your local agricultural extension officer.";
 
 
 
134
  }
135
 
136
- // Stream word by word
137
- const words = result.split(' ');
138
- for (let i = 0; i < words.length; i++) {
139
- const chunk = (i === 0 ? '' : ' ') + words[i];
140
- res.write(`data: ${JSON.stringify({ chunk })}\n\n`);
141
- await new Promise(r => setTimeout(r, 12));
142
- }
143
 
144
- res.write(`data: ${JSON.stringify({ done: true, result })}\n\n`);
145
- res.end();
146
 
147
  } catch (err) {
148
- res.write(`data: ${JSON.stringify({ error: err.message })}\n\n`);
149
- res.end();
 
150
  }
151
  });
152
  return;
 
9
  let generator;
10
  let knowledgeBase = [];
11
 
12
+ // ── KNOWLEDGE FILES ───────────────────────────────────────────────────────────
13
  function loadKnowledge() {
14
  if (!fs.existsSync(KNOWLEDGE_DIR)) { fs.mkdirSync(KNOWLEDGE_DIR); return; }
15
  const files = fs.readdirSync(KNOWLEDGE_DIR).filter(f => f.endsWith('.txt'));
 
19
  const chunks = splitChunks(content, 600, 60);
20
  chunks.forEach(c => knowledgeBase.push({ source: file, text: c }));
21
  }
22
+ console.log(`Knowledge loaded: ${files.length} files, ${knowledgeBase.length} chunks`);
23
  }
24
 
25
  function splitChunks(text, size, overlap) {
 
47
  .join('\n\n---\n\n');
48
  }
49
 
50
+ // ── MODEL ─────────────────────────────────────────────────────────────────────
51
  async function loadModel() {
52
  console.log("Loading model...");
53
  generator = await pipeline('text-generation', MODEL_NAME, { dtype: 'q4' });
 
61
  repetition_penalty: 1.2,
62
  do_sample: false
63
  });
64
+ // Extract only the assistant reply content
65
+ const generated = output[0].generated_text;
66
+ if (Array.isArray(generated)) {
67
+ return generated.at(-1)?.content || '';
68
+ }
69
+ return String(generated || '');
70
  }
71
 
72
  // ── SERVER ────────────────────────────────────────────────────────────────────
 
79
 
80
  const pathname = req.url.split('?')[0];
81
 
82
+ // Status check
83
  if (pathname === '/' && req.method === 'GET') {
84
  res.setHeader('Content-Type', 'application/json');
85
  res.writeHead(200);
86
+ return res.end(JSON.stringify({
87
+ status: "running",
88
+ model: MODEL_NAME,
89
+ knowledge_chunks: knowledgeBase.length
90
+ }));
91
  }
92
 
93
+ // Reload knowledge
94
  if (pathname === '/reload-knowledge' && req.method === 'POST') {
95
  loadKnowledge();
96
  res.setHeader('Content-Type', 'application/json');
 
98
  return res.end(JSON.stringify({ message: `Reloaded: ${knowledgeBase.length} chunks` }));
99
  }
100
 
101
+ // Main generate endpoint β€” returns plain JSON (no SSE, no streaming delays)
102
  if (pathname === '/generate' && req.method === 'POST') {
103
  let body = '';
104
  req.on('data', c => { body += c.toString(); });
105
  req.on('end', async () => {
106
+ res.setHeader('Content-Type', 'application/json');
107
+
108
  try {
109
  const { prompt, system } = JSON.parse(body);
110
+
111
  if (!generator) {
 
112
  res.writeHead(503);
113
+ return res.end(JSON.stringify({ error: "Model still loading, please wait..." }));
114
  }
115
 
116
+ // RAG: retrieve relevant knowledge chunks
117
  const ragContext = retrieveContext(prompt, 4);
118
  const ragSection = ragContext
119
+ ? `\n\nKNOWLEDGE BASE β€” use ONLY this information to answer:\n${ragContext}\n`
120
  : '';
121
 
122
+ // Build final system prompt
123
+ const finalSystem = (system || `You are Mlimi Connect AI, a free agricultural advisor for Malawian farmers. Only answer agriculture questions.`) + ragSection;
 
 
124
 
125
  const messages = [
126
  { role: 'system', content: finalSystem },
127
  { role: 'user', content: prompt }
128
  ];
129
 
130
+ console.log(`Generating response for: "${prompt.slice(0, 60)}..."`);
 
 
 
 
 
 
 
 
131
 
132
  let result = await generateResponse(messages);
133
 
134
+ // Fallback if model returns empty
135
+ if (!result || result.trim().length < 5) {
136
+ if (ragContext) {
137
+ result = `Here is what I know about this topic:\n\n${ragContext.slice(0, 600)}`;
138
+ } else {
139
+ result = "I don't have specific information on that topic. Please consult your local agricultural extension officer (AEO) for advice.";
140
+ }
141
  }
142
 
143
+ console.log(`Response ready: ${result.length} chars`);
 
 
 
 
 
 
144
 
145
+ res.writeHead(200);
146
+ res.end(JSON.stringify({ result }));
147
 
148
  } catch (err) {
149
+ console.error("Generation error:", err.message);
150
+ res.writeHead(500);
151
+ res.end(JSON.stringify({ error: err.message || "Generation failed" }));
152
  }
153
  });
154
  return;