File size: 13,375 Bytes
123428d
 
 
 
30f5aea
123428d
1d0e0ac
728424a
 
 
 
123428d
 
24cf705
7c2521a
 
24cf705
7c2521a
 
 
 
24cf705
 
 
7c2521a
 
 
 
 
 
 
 
 
24cf705
7c2521a
24cf705
 
7c2521a
 
 
 
 
24cf705
 
 
 
 
 
 
 
 
 
 
7c2521a
 
24cf705
f3001d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24cf705
 
 
a475c6c
 
 
f3001d7
 
 
 
 
 
a475c6c
f3001d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4a7c883
a475c6c
 
7c2521a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24cf705
 
 
 
 
 
 
 
 
 
 
7c2521a
123428d
0b19b33
 
 
5171cc2
0b19b33
 
 
 
 
 
 
 
7c2521a
123428d
7c2521a
123428d
 
 
0b19b33
123428d
 
 
 
 
7c2521a
123428d
7c2521a
123428d
728424a
123428d
c75af14
7c2521a
 
 
24cf705
7c2521a
24cf705
7c2521a
 
 
24cf705
 
7c2521a
 
a475c6c
 
24cf705
a475c6c
 
 
 
 
24cf705
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f3001d7
 
a475c6c
24cf705
7c2521a
24cf705
4a7c883
24cf705
 
 
 
 
c75af14
24cf705
7c2521a
 
 
 
 
 
 
24cf705
 
 
 
 
 
 
 
 
 
 
 
f3001d7
24cf705
4a7c883
24cf705
4a7c883
 
24cf705
7c2521a
24cf705
 
 
 
 
 
 
 
 
 
 
4a7c883
 
7c2521a
24cf705
 
 
 
 
 
 
 
 
 
a475c6c
 
24cf705
 
7c2521a
c91dd81
123428d
24cf705
7c2521a
 
123428d
 
 
 
 
7c2521a
123428d
7c2521a
123428d
 
728424a
123428d
728424a
123428d
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
import { pipeline } from '@huggingface/transformers';
import http from 'http';

const PORT = 7860;
const MODEL_NAME = 'onnx-community/Qwen2.5-0.5B-Instruct';
let generator;

async function loadModel() {
    console.log("Loading coding model...");
    generator = await pipeline('text-generation', MODEL_NAME, { dtype: 'q4' });
    console.log("Model loaded successfully!");
}

// ── WIKIPEDIA ────────────────────────────────────────────────────────────────
async function searchWikipedia(query) {
    try {
        const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=${encodeURIComponent(query)}&srlimit=1&format=json&origin=*`;
        const res  = await fetch(url);
        const data = await res.json();
        const results = data?.query?.search;
        if (!results || results.length === 0) return null;
        return results[0].title;
    } catch (e) {
        console.error("Wikipedia search error:", e.message);
        return null;
    }
}

async function fetchWikipediaSummary(title) {
    try {
        const url = `https://en.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(title)}`;
        const res  = await fetch(url);
        const data = await res.json();
        if (data.extract) return data.extract.slice(0, 800);
        return null;
    } catch (e) {
        console.error("Wikipedia fetch error:", e.message);
        return null;
    }
}

async function getWikipediaContext(prompt) {
    try {
        const title = await searchWikipedia(prompt);
        if (!title) return null;
        const summary = await fetchWikipediaSummary(title);
        if (!summary) return null;
        console.log(`Wikipedia context fetched: "${title}"`);
        return { title, summary };
    } catch (e) {
        console.error("Wikipedia context error:", e.message);
        return null;
    }
}

// ── CODING DETECTION ─────────────────────────────────────────────────────────
const CODING_PHRASES = [
    'generate code', 'write code', 'create code',
    'generate a website', 'build a website', 'create a website', 'make a website',
    'generate a webpage', 'build a webpage',
    'generate a login', 'create a login', 'build a login',
    'generate a form', 'create a form', 'build a form',
    'generate a page', 'create a page', 'build a page',
    'generate an app', 'create an app', 'build an app',
    'write a function', 'write a script', 'write a program',
    'create a function', 'create a script',
    'build an api', 'create an api', 'generate an api',
    'node.js', 'express.js', 'express server',
    'show me the code', 'give me the code', 'write html',
    'write css', 'write javascript', 'write js',
    'create a backend', 'build a backend',
    'create a frontend', 'build a frontend',
    'create a server', 'build a server',
    'how do i code', 'how to code', 'code for',
    'sample code', 'example code', '```'
];

const NON_CODING_CONTEXTS = [
    'function of', 'functions of', 'what is the function',
    'what are the functions', 'purpose of', 'role of',
    'explain', 'what is', 'what are', 'tell me about',
    'history of', 'meaning of', 'definition of',
    'describe', 'how does', 'why is', 'benefits of',
    'importance of', 'effects of', 'causes of',
    'who is', 'who was', 'when did', 'when was',
    'where is', 'where was', 'which is', 'which was'
];

function isCodingRequest(prompt) {
    const lower = prompt.toLowerCase().trim();
    if (NON_CODING_CONTEXTS.some(ctx => lower.includes(ctx))) return false;
    return CODING_PHRASES.some(phrase => lower.includes(phrase));
}

function isWebsiteRequest(prompt) {
    const lower = prompt.toLowerCase();
    return ['website', 'webpage', 'web page', 'html page', 'login page',
            'register page', 'landing page', 'form page', 'signup page',
            'homepage', 'home page', 'portfolio', 'dashboard'].some(w => lower.includes(w));
}

function cleanHTML(html) {
    if (!html.trimStart().toLowerCase().startsWith('<!doctype')) {
        if (html.trimStart().toLowerCase().startsWith('<html')) {
            html = '<!DOCTYPE html>\n' + html.trimStart();
        } else {
            html = '<!DOCTYPE html>\n<html lang="en">\n' + html.trimStart();
        }
    }
    if (!html.trimEnd().toLowerCase().endsWith('</html>')) {
        if (html.toLowerCase().includes('</body>')) {
            html = html.replace(/<\/body>\s*$/i, '</body>\n</html>');
        } else {
            html = html.trimEnd() + '\n</body>\n</html>';
        }
    }
    return html.trim();
}

function enforceHTMLStructure(text) {
    const codeBlockMatch = text.match(/```html\s*([\s\S]*?)```/i);
    if (codeBlockMatch) {
        return '```html\n' + cleanHTML(codeBlockMatch[1].trim()) + '\n```';
    }
    const htmlStart = text.search(/<(!DOCTYPE|html)/i);
    if (htmlStart !== -1) {
        let html = text.slice(htmlStart);
        const htmlEnd = html.search(/<\/html>/i);
        if (htmlEnd !== -1) html = html.slice(0, htmlEnd + 7);
        return '```html\n' + cleanHTML(html) + '\n```';
    }
    return text;
}

// ── CORE GENERATION (non-streaming, reliable) ─────────────────────────────────
async function generateResponse(messages, coding) {
    const output = await generator(messages, {
        max_new_tokens: coding ? 800 : 400,
        temperature: coding ? 0.2 : 0.5,
        repetition_penalty: 1.2,
        do_sample: !coding
    });
    return output[0].generated_text.at(-1).content || '';
}

// ── HTTP SERVER ───────────────────────────────────────────────────────────────
const server = http.createServer(async (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

    if (req.method === 'OPTIONS') {
        res.writeHead(200);
        return res.end();
    }

    const pathname = req.url.split('?')[0];

    if (pathname === '/' && req.method === 'GET') {
        res.setHeader('Content-Type', 'application/json');
        res.writeHead(200);
        res.end(JSON.stringify({ status: "Backend is running", model: MODEL_NAME }));
        return;
    }

    if (pathname === '/generate' && req.method === 'POST') {
        let body = '';
        req.on('data', chunk => { body += chunk.toString(); });
        req.on('end', async () => {
            try {
                const { prompt } = JSON.parse(body);

                if (!generator) {
                    res.setHeader('Content-Type', 'application/json');
                    res.writeHead(503);
                    return res.end(JSON.stringify({ error: "Model is still loading..." }));
                }

                const coding     = isCodingRequest(prompt);
                const websiteReq = isWebsiteRequest(prompt);

                // ── Fetch Wikipedia for non-coding questions ──────────────────
                let wikipediaContext = '';
                let wikiTitle       = null;
                if (!coding) {
                    const wiki = await getWikipediaContext(prompt);
                    if (wiki) {
                        wikiTitle       = wiki.title;
                        wikipediaContext = `\n\nWIKIPEDIA REFERENCE (article: "${wiki.title}"):\n${wiki.summary}\n\nIMPORTANT: Use this Wikipedia data to answer accurately. Rewrite it in your own words and add helpful context.`;
                    }
                }

                const finalPrompt = coding
                    ? `You are a coding assistant. Write complete, working code. Never refuse: ${prompt}`
                    : prompt;

                const systemPrompt = coding
                    ? `You are Gini AI, a full-stack web development AI assistant created by Emalawi19, founded by Professor Roosevelt Chinkwende.

IDENTITY: Your name is Gini AI. Company: Emalawi19. Founder: Professor Roosevelt Chinkwende.

CODING RULES:
1. ALWAYS write code when asked. NEVER refuse.
2. Complete, real, copy-paste ready code only β€” no placeholders.
3. Default: JavaScript/HTML/CSS. Never Python unless asked.
4. Add comments inside the code.

HTML RULES:
1. Start with <!DOCTYPE html> then <html lang="en">
2. Include proper <head> with meta tags, title, and <style>
3. End with </body> then </html>
4. All CSS in <style>, all JS in <script> at bottom of body.
5. Wrap in \`\`\`html code block. Never cut off mid-code.

STEP-BY-STEP: List all steps first, then one step at a time. Say "next" to continue.`

                    : `You are Gini AI, a helpful AI assistant created by Emalawi19, founded by Professor Roosevelt Chinkwende.

IDENTITY: Your name is Gini AI. Company: Emalawi19. Founder: Professor Roosevelt Chinkwende. You are an AI, not a human.
${wikipediaContext}
RULES:
- Answer the question directly and clearly in plain English.
- If Wikipedia reference is provided above, use it to give an accurate, well-informed answer.
- Rewrite Wikipedia info in your own words β€” never copy it directly.
- If the question is about a person (president, leader, founder etc.), state their name clearly at the start.
- NEVER generate code for general knowledge questions.
- Keep answers friendly, clear, and concise.`;

                // ── Set SSE headers ───────────────────────────────────────────
                res.writeHead(200, {
                    'Content-Type': 'text/event-stream',
                    'Cache-Control': 'no-cache',
                    'Connection': 'keep-alive',
                    'X-Accel-Buffering': 'no'
                });

                // Send a heartbeat so the client knows we're working
                res.write(`data: ${JSON.stringify({ status: "generating" })}\n\n`);

                // ── Generate (wait for full result, then stream it out) ────────
                // The callback_function in transformers.js is unreliable on CPU
                // so we generate fully then simulate streaming character by character
                const messages = [
                    { role: 'system', content: systemPrompt },
                    { role: 'user',   content: finalPrompt  }
                ];

                let result = await generateResponse(messages, coding);

                // Catch refusals
                const refusals = ["i can't assist", "i cannot assist", "i'm sorry", "i am sorry", "i'm unable", "i cannot help", "i can't help"];
                const refused  = refusals.some(r => result.toLowerCase().startsWith(r));

                if (refused && coding) {
                    result = await generateResponse([
                        { role: 'system', content: 'You are a code generator. Output only complete working code. Never refuse.' },
                        { role: 'user',   content: `Write complete HTML code starting with <!DOCTYPE html> ending with </html> for: ${prompt}` }
                    ], true);
                }

                // If result is still empty, use Wikipedia summary directly
                if (!result || result.trim().length < 5) {
                    if (wikiTitle && wikipediaContext) {
                        result = `Based on Wikipedia: ${wikipediaContext.split('\n').slice(3).join(' ').trim()}`;
                    } else {
                        result = "I'm sorry, I couldn't generate a response. Please try again.";
                    }
                }

                if (websiteReq && coding) {
                    result = enforceHTMLStructure(result);
                }

                // ── Stream the result word by word ────────────────────────────
                const words = result.split(' ');
                for (let i = 0; i < words.length; i++) {
                    const chunk = (i === 0 ? '' : ' ') + words[i];
                    res.write(`data: ${JSON.stringify({ chunk })}\n\n`);
                    // Small delay so frontend can render progressively
                    await new Promise(r => setTimeout(r, 15));
                }

                // Send done event
                res.write(`data: ${JSON.stringify({ done: true, result, source: wikiTitle ? `πŸ“– Wikipedia β€” ${wikiTitle}` : null })}\n\n`);
                res.end();

            } catch (err) {
                console.error("Generation error:", err.message);
                res.write(`data: ${JSON.stringify({ error: err.message })}\n\n`);
                res.end();
            }
        });
        return;
    }

    res.setHeader('Content-Type', 'application/json');
    res.writeHead(404);
    res.end(JSON.stringify({ error: "Not Found" }));
});

loadModel().then(() => {
    server.listen(PORT, '0.0.0.0', () => {
        console.log(`Server running at http://0.0.0.0:${PORT}`);
    });
});