profileShare

rasmusjy / roundtable

Read-only snapshot

No repository description.

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

Commit

show estimated cost in debate builder

commit 22505ed

1 changed file with +27 and −2

modified src/components/debate/new-debate.tsx +27 −2
@@ -1,7 +1,7 @@
1 1 'use client';
2 2
3 3 import { AlertTriangle, Loader2, Play, RotateCcw, Save, Sparkles, Square } from 'lucide-react';
4 -import { useEffect, useState } from 'react';
4 +import { useEffect, useMemo, useState } from 'react';
5 5 import { DebateConsole } from '@/components/debate/debate-console';
6 6 import { ModelCombobox } from '@/components/debate/model-combobox';
7 7 import { ModelPicker } from '@/components/debate/model-picker';
@@ -15,6 +15,8 @@import { toast } from '@/components/ui/sonner';
15 15 import { chairmanSharesProvider, DEFAULT_CONVERGENCE_MODEL } from '@/core/models';
16 16 import { useDebateStream } from '@/hooks/use-debate-stream';
17 17 import { useModels } from '@/hooks/use-models';
18 +import { estimateDebateCostUsd } from '@/lib/cost-estimate';
19 +import { formatUsd } from '@/lib/utils';
18 20
19 21 interface Preset {
20 22 id: string;
@@ -71,6 +73,24 @@export function NewDebate() {
71 73 const canStart = question.trim().length >= 3 && council.length >= 3 && council.length <= 6 && Boolean(chairman);
72 74 const running = phase === 'connecting' || phase === 'streaming';
73 75
76 + const priceMap = useMemo(
77 + () => new Map(models.map((m) => [m.id, { prompt: m.promptPrice, completion: m.completionPrice }])),
78 + [models],
79 + );
80 + const estimate = useMemo(
81 + () =>
82 + canStart
83 + ? estimateDebateCostUsd({
84 + councilModels: council,
85 + chairmanModel: chairman,
86 + convergenceModel,
87 + rounds: maxRounds,
88 + price: (id) => priceMap.get(id),
89 + })
90 + : 0,
91 + [canStart, council, chairman, convergenceModel, maxRounds, priceMap],
92 + );
93 +
74 94 const applyPreset = (p: Preset) => {
75 95 setCouncil(p.models);
76 96 setChairman(p.chairmanModel);
@@ -233,7 +253,12 @@export function NewDebate() {
233 253 </div>
234 254 )}
235 255
236 - <div className="sticky bottom-4 flex justify-end">
256 + <div className="sticky bottom-4 flex items-center justify-end gap-3">
257 + {canStart && (
258 + <span className="rounded-md bg-background/80 px-2.5 py-1.5 text-xs text-muted-foreground shadow-sm backdrop-blur">
259 + ~{formatUsd(estimate)} est. ยท up to {maxRounds} {maxRounds === 1 ? 'round' : 'rounds'}
260 + </span>
261 + )}
237 262 <Button size="lg" disabled={!canStart} onClick={launch} className="shadow-lg">
238 263 <Play className="h-4 w-4" /> Start debate
239 264 </Button>