profileShare

rasmusjy / roundtable

Read-only snapshot

No repository description.

main default branch 181 files Expires Sep 13, 2026, 9:06 AM

Commit

add debate cli to showcase the core engine

commit 991ac22

2 changed files with +68 and −1

Jump to a changed file
  1. package.json +2 −1
  2. scripts/debate-cli.ts +66 −0
modified package.json +2 −1
@@ -23,7 +23,8 @@
23 23 "prisma:generate": "prisma generate",
24 24 "prisma:migrate": "prisma migrate deploy",
25 25 "prisma:push": "prisma db push",
26 - "db:seed": "tsx prisma/seed.ts"
26 + "db:seed": "tsx prisma/seed.ts",
27 + "debate": "tsx scripts/debate-cli.ts"
27 28 },
28 29 "prisma": {
29 30 "seed": "tsx prisma/seed.ts"
added scripts/debate-cli.ts +66 −0
@@ -0,0 +1,66 @@
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 { resolveDebateConfig } 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 = resolveDebateConfig({
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 + console.log(
59 + `\nRounds: ${result.totals.rounds} | Cost: $${result.totals.costUsd.toFixed(4)} | Status: ${result.status}\n`,
60 + );
61 +}
62 +
63 +main().catch((err) => {
64 + console.error(err);
65 + process.exit(1);
66 +});