File size: 7,163 Bytes
fc851c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
const express = require('express');
const path = require('path');
const cors = require('cors');
const { MODELS, callGroq, callGeminiDebater, callArchitect } = require('./api');

const app = express();
const PORT = process.env.PORT || 3000;

app.use(cors());
app.use(express.json());
app.use(express.static(path.join(__dirname, '../public')));

// Debate Endpoint
app.post('/api/debate', async (req, res) => {
    const { topic } = req.body;
    
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');

    const sendEvent = (type, data) => {
        res.write(`event: ${type}\ndata: ${JSON.stringify(data)}\n\n`);
    };

    sendEvent('status', { message: 'Debate Initiated', topic });
    let transcript = "";

    // ROUND 1
    sendEvent('status', { message: 'Round 1: Opening Arguments...' });
    let round1Responses = {};
    for (const model of MODELS) {
        sendEvent('model_start', { round: 1, model });
        let content = "";
        let finalSystemPrompt = `You are a critical thinker and expert advisor taking part in a debate. Give your best, concise opening argument on this topic. Don't be cliché, be brave, direct, and specific. Keep to ~150 words.`;
        
        if (model.includes('gemini')) {
            content = await callGeminiDebater(model, finalSystemPrompt, `Topic: ${topic}`);
        } else {
            if (model.includes('qwen')) {
                finalSystemPrompt += "\n\nIMPORTANT: Output ONLY valid JSON in the format {\"argument\": \"your final polished response without any thinking\"}.";
            }
            content = await callGroq(model, finalSystemPrompt, `Topic: ${topic}`);
            if (model.includes('qwen')) {
                try {
                    const jsonMatch = content.match(/\{[\s\S]*\}/);
                    if (jsonMatch) content = JSON.parse(jsonMatch[0]).argument;
                } catch(e) {}
            }
        }

        round1Responses[model] = content;
        sendEvent('model_end', { round: 1, model, content });
        // small 1s delay to respect free tier rate limit
        await new Promise(resolve => setTimeout(resolve, 1000));
    }
    
    transcript += "\n--- ROUND 1: OPENING ARGUMENTS ---\n";
    for(let m of MODELS) { transcript += `${m}:\n${round1Responses[m]}\n\n`; }

    // ROUND 2
    sendEvent('status', { message: 'Round 2: Rebuttals...' });
    let round2Responses = {};
    for (const model of MODELS) {
        sendEvent('model_start', { round: 2, model });
        
        let otherArguments = "";
        for (const [m, r] of Object.entries(round1Responses)) {
            if (m !== model) {
                otherArguments += `${m} argued: "${r}"\n\n`;
            }
        }

        let content = "";
        let finalSystemPrompt = `You are a critical debater. Review the opening arguments of the other AI models. Point out flaws, logical fallacies, or forcefully expand on weak points in their arguments. Be aggressive but intellectual. Keep it under ~150 words.`;
        const userPrompt = `Topic: ${topic}\n\nHere is what the others said:\n${otherArguments}\n\nDeliver your rebuttal.`;
        
        if (model.includes('gemini')) {
            content = await callGeminiDebater(model, finalSystemPrompt, userPrompt);
        } else {
            if (model.includes('qwen')) {
                finalSystemPrompt += "\n\nIMPORTANT: Output ONLY valid JSON in the format {\"argument\": \"your final polished rebuttal without any thinking\"}.";
            }
            content = await callGroq(model, finalSystemPrompt, userPrompt);
            if (model.includes('qwen')) {
                try {
                    const jsonMatch = content.match(/\{[\s\S]*\}/);
                    if (jsonMatch) content = JSON.parse(jsonMatch[0]).argument;
                } catch(e) {}
            }
        }

        round2Responses[model] = content;
        sendEvent('model_end', { round: 2, model, content });
        await new Promise(resolve => setTimeout(resolve, 1000));
    }

    transcript += "\n--- ROUND 2: REBUTTALS ---\n";
    for(let m of MODELS) { transcript += `${m}:\n${round2Responses[m]}\n\n`; }

    // ROUND 3
    sendEvent('status', { message: 'Round 3: Final Responses & Defense...' });
    let round3Responses = {};
    for (const model of MODELS) {
        sendEvent('model_start', { round: 3, model });
        
        let otherRebuttals = "";
        for (const [m, r] of Object.entries(round2Responses)) {
            if (m !== model) {
                otherRebuttals += `${m} critiqued: "${r}"\n\n`;
            }
        }

        let content = "";
        let finalSystemPrompt = `This is your final defense. Acknowledge valid critiques but firmly defend your core premise based on the other models' rebuttals to the group. Give your concluding stance. Max 150 words.`;
        const userPrompt = `Topic: ${topic}\n\nHere are the critiques from others in the last round:\n${otherRebuttals}\n\nDeliver your final defense and conclusion.`;
        
        if (model.includes('gemini')) {
            content = await callGeminiDebater(model, finalSystemPrompt, userPrompt);
        } else {
            if (model.includes('qwen')) {
                finalSystemPrompt += "\n\nIMPORTANT: Output ONLY valid JSON in the format {\"argument\": \"your final polished defense without any thinking\"}.";
            }
            content = await callGroq(model, finalSystemPrompt, userPrompt);
            if (model.includes('qwen')) {
                try {
                    const jsonMatch = content.match(/\{[\s\S]*\}/);
                    if (jsonMatch) content = JSON.parse(jsonMatch[0]).argument;
                } catch(e) {}
            }
        }

        round3Responses[model] = content;
        sendEvent('model_end', { round: 3, model, content });
        await new Promise(resolve => setTimeout(resolve, 1000));
    }

    transcript += "\n--- ROUND 3: FINAL DEFENSE ---\n";
    for(let m of MODELS) { transcript += `${m}:\n${round3Responses[m]}\n\n`; }

    // FINAL VERDICT
    sendEvent('status', { message: 'Synthesizing Final Verdict via Gemma 4...' });
    sendEvent('model_start', { round: 'Final', model: 'Gemma 4 (Architect)' });
    
    const geminiSystemPrompt = `You are the Lead Architect and final judge of this debate. Review the transcript of the 3-round debate across the multiple models. 
Your goal:
1. Synthesize the most compelling arguments.
2. Call out specific models if they had a brilliant insight or if they hallucinated/made a weak/false point.
3. Deliver the unvarnished, brutal truth final verdict on the topic. Be decisive. Do not sit on the fence.
Format your output beautifully in Markdown.`;

    const geminiVerdict = await callArchitect(geminiSystemPrompt, transcript);
    sendEvent('model_end', { round: 'Final', model: 'Gemma 4 (Architect)', content: geminiVerdict });

    // End Stream
    sendEvent('done', { message: 'Debate Concluded' });
    res.end();
});

app.listen(PORT, () => {
    console.log(`AI Debate Engine running on http://localhost:${PORT}`);
});