profileShare

rasmusjy / roundtable

Read-only snapshot

No repository description.

main default branch 181 files Expires Sep 13, 2026, 9:06 AM
cost-estimate.ts 1,772 bytes
1 /**
2 * Rough upfront cost estimate for a debate, so the builder can warn before a
3 * user spends real credit. Uses typical per-stage token counts; the actual cost
4 * is usually lower because convergence can stop the debate early.
5 */
6 export interface TokenPrice {
7 /** USD per prompt token. */
8 prompt: number;
9 /** USD per completion token. */
10 completion: number;
11 }
12
13 type Stage = 'answer' | 'critique' | 'revision' | 'convergence' | 'synthesis';
14
15 const AVG: Record<Stage, { prompt: number; completion: number }> = {
16 answer: { prompt: 250, completion: 320 },
17 critique: { prompt: 850, completion: 260 },
18 revision: { prompt: 950, completion: 320 },
19 convergence: { prompt: 1100, completion: 160 },
20 synthesis: { prompt: 1300, completion: 420 },
21 };
22
23 const FALLBACK: TokenPrice = { prompt: 1e-6, completion: 3e-6 };
24
25 export interface EstimateInput {
26 councilModels: string[];
27 chairmanModel: string;
28 convergenceModel: string;
29 /** Maximum number of critique->revision rounds. */
30 rounds: number;
31 price: (modelId: string) => TokenPrice | undefined;
32 }
33
34 /** Estimated *maximum* USD cost (all rounds run, no early stop). */
35 export function estimateDebateCostUsd(input: EstimateInput): number {
36 const cost = (modelId: string, stage: Stage) => {
37 const p = input.price(modelId) ?? FALLBACK;
38 return AVG[stage].prompt * p.prompt + AVG[stage].completion * p.completion;
39 };
40
41 let total = 0;
42 for (const m of input.councilModels) total += cost(m, 'answer');
43 for (let r = 0; r < input.rounds; r++) {
44 for (const m of input.councilModels) {
45 total += cost(m, 'critique');
46 total += cost(m, 'revision');
47 }
48 total += cost(input.convergenceModel, 'convergence');
49 }
50 total += cost(input.chairmanModel, 'synthesis');
51 return total;
52 }
53