Commit
add debate cli to showcase the core engine
commit
991ac22
2 changed files with +68 and −1
Jump to a changed file
- package.json +2 −1
- 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 | +}); |