profileShare

rasmusjy / roundtable

Read-only snapshot

No repository description.

main default branch 181 files Expires Sep 13, 2026, 9:06 AM
export-markdown.test.ts 4,237 bytes
1 import { describe, expect, it } from 'vitest';
2 import { emptyUsage, type DebateResult } from '@/core/types';
3 import { debateToMarkdown } from './export-markdown';
4
5 function makeResult(): DebateResult {
6 const config = {
7 question: 'Monolith or microservices?',
8 models: ['openai/gpt-4o', 'anthropic/claude-3.5-sonnet'],
9 chairmanModel: 'x-ai/grok-2',
10 convergenceModel: 'google/gemini-2.0-flash-001',
11 maxRounds: 2,
12 convergenceThreshold: 85,
13 temperature: 0.7,
14 perModelTimeoutMs: 90_000,
15 };
16 const participants = [
17 { id: 'p0', model: 'openai/gpt-4o', displayName: 'GPT 4o' },
18 { id: 'p1', model: 'anthropic/claude-3.5-sonnet', displayName: 'Claude 3.5 Sonnet' },
19 ];
20 return {
21 debateId: 'd1',
22 config,
23 participants,
24 status: 'completed',
25 initialAnswers: [
26 { participantId: 'p0', model: 'openai/gpt-4o', round: 0, content: 'Monolith.', usage: emptyUsage(), latencyMs: 0 },
27 { participantId: 'p1', model: 'anthropic/claude-3.5-sonnet', round: 0, content: 'Depends.', usage: emptyUsage(), latencyMs: 0 },
28 ],
29 rounds: [
30 {
31 round: 1,
32 critiques: [
33 {
34 round: 1,
35 reviewerParticipantId: 'p0',
36 reviewerModel: 'openai/gpt-4o',
37 reviews: [{ label: 'A', targetParticipantId: 'p1', weaknesses: ['vague'], strengths: [], score: 6, justification: 'meh' }],
38 usage: emptyUsage(),
39 latencyMs: 0,
40 },
41 ],
42 revisions: [
43 {
44 round: 1,
45 participantId: 'p1',
46 model: 'anthropic/claude-3.5-sonnet',
47 content: 'Monolith, with caveats.',
48 changelog: { changed: true, summary: 'narrowed', bullets: ['a'] },
49 usage: emptyUsage(),
50 latencyMs: 0,
51 },
52 ],
53 convergence: { round: 1, model: 'google/gemini-2.0-flash-001', score: 88, converged: true, disagreements: [], usage: emptyUsage(), latencyMs: 0 },
54 },
55 ],
56 synthesis: {
57 model: 'x-ai/grok-2',
58 finalAnswer: 'Start with a modular monolith.',
59 dissent: [{ topic: 'edge case', positions: [{ participantId: 'p0', model: 'openai/gpt-4o', position: 'no split' }] }],
60 usage: emptyUsage(),
61 latencyMs: 0,
62 },
63 provenance: {
64 model: 'google/gemini-2.0-flash-001',
65 round: 1,
66 claims: [
67 {
68 text: 'Start with a monolith.',
69 supportedBy: [{ participantId: 'p0', model: 'openai/gpt-4o' }],
70 contestedBy: [],
71 unsourced: false,
72 },
73 { text: 'Revisit in a year.', supportedBy: [], contestedBy: [], unsourced: true },
74 ],
75 usage: emptyUsage(),
76 latencyMs: 0,
77 },
78 failures: [],
79 finalAnswers: [],
80 totals: { costUsd: 0.02, promptTokens: 100, completionTokens: 200, rounds: 1, durationMs: 5000, costByModel: {} },
81 };
82 }
83
84 describe('debateToMarkdown', () => {
85 const md = debateToMarkdown(makeResult());
86
87 it('includes the question and council', () => {
88 expect(md).toContain('# Roundtable Deliberation Report');
89 expect(md).toContain('Monolith or microservices?');
90 expect(md).toContain('GPT 4o');
91 expect(md).toContain('`x-ai/grok-2`');
92 });
93
94 it('includes the final answer and dissent', () => {
95 expect(md).toContain('## Final Answer');
96 expect(md).toContain('Start with a modular monolith.');
97 expect(md).toContain('Dissent Report');
98 expect(md).toContain('no split');
99 });
100
101 it('renders the round with a critique score table and revision', () => {
102 expect(md).toContain('## Round 1');
103 expect(md).toContain('Critique scores');
104 expect(md).toContain('| GPT 4o |'); // reviewer row
105 expect(md).toContain('Monolith, with caveats.');
106 expect(md).toContain('Convergence score: 88/100');
107 });
108
109 it('renders the claim check with sourced and unsourced claims', () => {
110 expect(md).toContain('### Claim Check');
111 expect(md).toContain('Start with a monolith. (supported by GPT 4o)');
112 expect(md).toContain("Revisit in a year. (**chairman's own addition");
113 });
114
115 it('renders round 0 answers', () => {
116 expect(md).toContain('## Round 0');
117 expect(md).toContain('Monolith.');
118 expect(md).toContain('Depends.');
119 });
120 });
121