profileShare

rasmusjy / roundtable

Read-only snapshot

No repository description.

main default branch 181 files Expires Sep 13, 2026, 9:06 AM
debate-cli.ts 2,815 bytes
1 /* eslint-disable no-console */
2 /**
3 * Run a debate from the terminal, with no Next.js, database, or network:
4 *
5 * pnpm debate "Is a modular monolith better than microservices for a small team?"
6 *
7 * This exists to demonstrate that the debate engine in src/core is genuinely
8 * framework-agnostic - the same orchestrator the web app drives over SSE runs
9 * here against the deterministic mock client. Swap MockLlmClient for the
10 * OpenRouter client to run it for real.
11 */
12 import { parseDebateConfig } from '@/core/config';
13 import { MockLlmClient } from '@/core/mock-client';
14 import { runDebate } from '@/core/orchestrator';
15 import { displayNameForModel } from '@/core/types';
16
17 const question =
18 process.argv.slice(2).join(' ').trim() ||
19 'Is a modular monolith better than microservices for a four-person startup?';
20
21 const config = parseDebateConfig({
22 question,
23 models: ['openai/gpt-4o', 'anthropic/claude-3.5-sonnet', 'google/gemini-pro-1.5'],
24 chairmanModel: 'x-ai/grok-2-1212',
25 maxRounds: 2,
26 convergenceThreshold: 85,
27 temperature: 0.7,
28 });
29
30 async function main() {
31 console.log(`\nQuestion: ${question}\nCouncil: ${config.models.map(displayNameForModel).join(', ')}`);
32
33 const result = await runDebate(
34 config,
35 {
36 llm: new MockLlmClient({ convergenceBase: 62, convergenceStep: 16 }),
37 emit: (e) => {
38 if (e.type === 'round_started') console.log(`\n=== Round ${e.round} (${e.kind}) ===`);
39 else if (e.type === 'answer_completed') console.log(` answer ${displayNameForModel(e.record.model)}`);
40 else if (e.type === 'critique_completed')
41 console.log(` critique ${displayNameForModel(e.record.reviewerModel)}`);
42 else if (e.type === 'revision_completed')
43 console.log(
44 ` revision ${displayNameForModel(e.record.model)} (${e.record.changelog.changed ? 'changed' : 'defended'})`,
45 );
46 else if (e.type === 'convergence_result') console.log(` converge score ${e.record.score}/100`);
47 },
48 },
49 { debateId: 'cli' },
50 );
51
52 console.log('\n================ FINAL ANSWER ================\n');
53 console.log(result.synthesis?.finalAnswer ?? '(no synthesis)');
54 if (result.synthesis?.dissent.length) {
55 console.log('\n---- Dissent ----');
56 for (const d of result.synthesis.dissent) console.log(` - ${d.topic}`);
57 }
58 const unsourced = result.provenance?.claims.filter((c) => c.unsourced) ?? [];
59 if (unsourced.length) {
60 console.log('\n---- Chairman additions (claims no council member made) ----');
61 for (const c of unsourced) console.log(` - ${c.text}`);
62 }
63 console.log(
64 `\nRounds: ${result.totals.rounds} | Cost: $${result.totals.costUsd.toFixed(4)} | Status: ${result.status}\n`,
65 );
66 }
67
68 main().catch((err) => {
69 console.error(err);
70 process.exit(1);
71 });
72