Skip to content

Commit 13b491d

Browse files
committed
fix: resolve all SonarCloud issues across Python + React frontend
1 parent 1899eb4 commit 13b491d

38 files changed

Lines changed: 981 additions & 925 deletions

benchmarks/ablation_benchmark.py

Lines changed: 198 additions & 171 deletions
Large diffs are not rendered by default.

benchmarks/micro_benchmark.py

Lines changed: 135 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,129 @@ async def run_certamen_tournament(
132132
}
133133

134134

135+
_SECTION_DIVIDER = "---\n\n"
136+
137+
138+
async def _run_single_models(
139+
certamen: Certamen, question: str
140+
) -> list[dict[str, Any]]:
141+
results = []
142+
for model_name in certamen.healthy_models:
143+
print("\n" + "=" * 80)
144+
print(f"APPROACH 1: Single Model ({model_name}) + Chain-of-Thought")
145+
print("=" * 80 + "\n")
146+
try:
147+
result = await run_single_model_with_cot(
148+
question, model_name, certamen
149+
)
150+
results.append(result)
151+
print("\n✅ Complete!")
152+
print(f" Duration: {result['duration_seconds']:.1f}s")
153+
print(f" Cost: ${result['cost_estimate']:.4f}")
154+
if not result["cost_estimate"]:
155+
print(" (Local/free model - no API costs)")
156+
except Exception as e:
157+
logger.error(f"Failed to run {model_name}: {e}")
158+
print(f"\n❌ Failed: {e}")
159+
print(" Continuing with other models...")
160+
return results
161+
162+
163+
def _print_cost_summary(
164+
single_model_results: list[dict[str, Any]],
165+
tournament_result: dict[str, Any],
166+
) -> None:
167+
if not single_model_results:
168+
return
169+
print("\n📊 Cost Summary:")
170+
print("-" * 80)
171+
for i, result in enumerate(single_model_results):
172+
print(
173+
f" Single Model #{i + 1} ({result['model']}): ${result['cost_estimate']:.4f}"
174+
)
175+
print(f" Certamen Tournament: ${tournament_result['cost_actual']:.4f}")
176+
avg_single_cost = sum(
177+
r["cost_estimate"] for r in single_model_results
178+
) / len(single_model_results)
179+
if avg_single_cost > 0:
180+
print(
181+
f"\n Tournament vs Avg Single Model: {tournament_result['cost_actual'] / avg_single_cost:.2f}x cost"
182+
)
183+
print("-" * 80)
184+
185+
186+
def _write_micro_report(
187+
output_path: Path,
188+
question: str,
189+
config_path: str,
190+
single_model_results: list[dict[str, Any]],
191+
tournament_result: dict[str, Any],
192+
) -> None:
193+
with open(output_path, "w") as f:
194+
f.write(
195+
"# Micro-Benchmark Results: Single Model vs Certamen Framework\n\n"
196+
)
197+
f.write(f"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
198+
f.write(f"Config: `{config_path}`\n\n")
199+
f.write("## Test Question\n\n")
200+
f.write(f"{question}\n\n")
201+
f.write(_SECTION_DIVIDER)
202+
203+
for i, single_result in enumerate(single_model_results):
204+
f.write(f"## Approach 1.{i + 1}: Single Model\n\n")
205+
f.write(f"Model: {single_result['model']}\n\n")
206+
f.write(
207+
f"Duration: {single_result['duration_seconds']:.1f} seconds\n\n"
208+
)
209+
f.write(f"Cost: ${single_result['cost_estimate']:.4f}\n\n")
210+
f.write("### Response\n\n")
211+
f.write(f"{single_result['response']}\n\n")
212+
213+
f.write(_SECTION_DIVIDER)
214+
f.write("## Approach 2: Certamen Framework Tournament\n\n")
215+
f.write(f"Champion: {tournament_result['champion_model']}\n\n")
216+
f.write(
217+
f"Duration: {tournament_result['duration_seconds']:.1f} seconds "
218+
f"({tournament_result['duration_seconds'] / 60:.1f} minutes)\n\n"
219+
)
220+
f.write(f"Total Cost: ${tournament_result['cost_actual']:.4f}\n\n")
221+
222+
if tournament_result.get("cost_by_model"):
223+
f.write("### Cost Breakdown by Model\n\n")
224+
for model_key, cost in tournament_result["cost_by_model"].items():
225+
f.write(f"- {model_key}: ${cost:.4f}\n")
226+
f.write("\n")
227+
228+
if single_model_results:
229+
cost_estimate = single_model_results[0]["cost_estimate"]
230+
cost_actual = tournament_result["cost_actual"]
231+
cost_multiple = (
232+
f"{cost_actual / cost_estimate:.1f}x"
233+
if cost_estimate > 0
234+
else "N/A (free/local baseline)"
235+
)
236+
f.write(f"Cost Multiple vs Single Model: {cost_multiple}\n\n")
237+
f.write(
238+
f"Time Multiple vs Single Model: "
239+
f"{tournament_result['duration_seconds'] / single_model_results[0]['duration_seconds']:.1f}x\n\n"
240+
)
241+
242+
if tournament_result["eliminated_models"]:
243+
f.write(
244+
f"Eliminated Models: {', '.join(str(m) for m in tournament_result['eliminated_models'])}\n\n"
245+
)
246+
247+
f.write("### Champion Response\n\n")
248+
f.write(f"{tournament_result['response']}\n\n")
249+
f.write(_SECTION_DIVIDER)
250+
251+
model_names = [res["model"] for res in single_model_results] + [
252+
"Certamen Framework"
253+
]
254+
f.write(generate_manual_evaluation_template(model_names))
255+
256+
135257
async def main(args: dict[str, Any] | None = None) -> None:
136-
"""Run micro-benchmark."""
137258
if args is None:
138259
parser = argparse.ArgumentParser(
139260
description="Micro-Benchmark: Single Model vs Certamen Framework"
@@ -145,7 +266,7 @@ async def main(args: dict[str, Any] | None = None) -> None:
145266
help="Path to configuration file (REQUIRED - no defaults)",
146267
)
147268
parsed_args = parser.parse_args()
148-
args = vars(parsed_args) # Convert to dict
269+
args = vars(parsed_args)
149270

150271
config_path = args.get("config")
151272
if not config_path:
@@ -155,21 +276,16 @@ async def main(args: dict[str, Any] | None = None) -> None:
155276
"The framework does not use fallback configurations."
156277
)
157278

158-
# Load config and inject outputs_dir
159279
from certamen.config.loader import Config
160280

161281
config_obj = Config(config_path)
162282
if not config_obj.load():
163283
raise RuntimeError(f"Failed to load configuration from {config_path}")
164284

165-
# Benchmark explicitly sets outputs_dir to current directory
166285
config_obj.config_data["outputs_dir"] = "."
167-
168-
# Initialize Certamen (loads config, secrets, models, runs health check)
169286
logger.info("Initializing Certamen...")
170287
certamen = await Certamen.from_settings(settings=config_obj.config_data)
171288

172-
# Get question from config
173289
question = certamen.config.config_data.get("question")
174290
if not question:
175291
raise ValueError(
@@ -190,7 +306,6 @@ async def main(args: dict[str, Any] | None = None) -> None:
190306
print(f"\nTest Question:\n{question}")
191307
print()
192308

193-
# Check if we have any healthy models
194309
if not certamen.is_ready:
195310
print("\n❌ ERROR: No healthy models available")
196311
if certamen.failed_models:
@@ -205,31 +320,8 @@ async def main(args: dict[str, Any] | None = None) -> None:
205320
for model_key, error in certamen.failed_models.items():
206321
print(f" - {model_key}: {error}")
207322

208-
single_model_results = []
323+
single_model_results = await _run_single_models(certamen, question)
209324

210-
# Run single models
211-
for model_name in certamen.healthy_models:
212-
print("\n" + "=" * 80)
213-
print(f"APPROACH 1: Single Model ({model_name}) + Chain-of-Thought")
214-
print("=" * 80 + "\n")
215-
216-
try:
217-
single_result = await run_single_model_with_cot(
218-
question, model_name, certamen
219-
)
220-
single_model_results.append(single_result)
221-
222-
print("\n✅ Complete!")
223-
print(f" Duration: {single_result['duration_seconds']:.1f}s")
224-
print(f" Cost: ${single_result['cost_estimate']:.4f}")
225-
if single_result["cost_estimate"] == 0.0:
226-
print(" (Local/free model - no API costs)")
227-
except Exception as e:
228-
logger.error(f"Failed to run {model_name}: {e}")
229-
print(f"\n❌ Failed: {e}")
230-
print(" Continuing with other models...")
231-
232-
# Run tournament
233325
print("\n" + "=" * 80)
234326
print("APPROACH 2: Certamen Framework Tournament")
235327
print("=" * 80 + "\n")
@@ -238,114 +330,34 @@ async def main(args: dict[str, Any] | None = None) -> None:
238330

239331
print("\n✅ Complete!")
240332
print(
241-
f" Duration: {tournament_result['duration_seconds']:.1f}s ({tournament_result['duration_seconds'] / 60:.1f} min)"
333+
f" Duration: {tournament_result['duration_seconds']:.1f}s "
334+
f"({tournament_result['duration_seconds'] / 60:.1f} min)"
242335
)
243336
print(f" Total Cost: ${tournament_result['cost_actual']:.4f}")
244337

245-
# Show cost breakdown
246338
if tournament_result.get("cost_by_model"):
247339
print("\n Cost Breakdown:")
248340
for model_key, cost in tournament_result["cost_by_model"].items():
249341
print(f" - {model_key}: ${cost:.4f}")
250342

251-
# Save results
252343
print("\n" + "=" * 80)
253344
print("BENCHMARK RESULTS")
254345
print("=" * 80)
255346

256-
# Print cost summary
257-
if single_model_results:
258-
print("\n📊 Cost Summary:")
259-
print("-" * 80)
260-
for i, result in enumerate(single_model_results):
261-
print(
262-
f" Single Model #{i + 1} ({result['model']}): ${result['cost_estimate']:.4f}"
263-
)
264-
print(
265-
f" Certamen Tournament: ${tournament_result['cost_actual']:.4f}"
266-
)
347+
_print_cost_summary(single_model_results, tournament_result)
267348

268-
# Show comparison
269-
avg_single_cost = sum(
270-
r["cost_estimate"] for r in single_model_results
271-
) / len(single_model_results)
272-
if avg_single_cost > 0:
273-
print(
274-
f"\n Tournament vs Avg Single Model: {tournament_result['cost_actual'] / avg_single_cost:.2f}x cost"
275-
)
276-
print("-" * 80)
277-
278-
# Save reports relative to project root
279349
reports_dir = Path(__file__).parent / "reports"
280350
reports_dir.mkdir(parents=True, exist_ok=True)
281351
output_path = reports_dir / "micro_benchmark_results.md"
282352

283-
with open(output_path, "w") as f:
284-
f.write(
285-
"# Micro-Benchmark Results: Single Model vs Certamen Framework\n\n"
286-
)
287-
f.write(f"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
288-
f.write(f"Config: `{config_path}`\n\n")
289-
290-
f.write("## Test Question\n\n")
291-
f.write(f"{question}\n\n")
292-
293-
f.write("---\n\n")
294-
295-
for i, single_result in enumerate(single_model_results):
296-
f.write(f"## Approach 1.{i + 1}: Single Model\n\n")
297-
f.write(f"Model: {single_result['model']}\n\n")
298-
f.write(
299-
f"Duration: {single_result['duration_seconds']:.1f} seconds\n\n"
300-
)
301-
f.write(f"Cost: ${single_result['cost_estimate']:.4f}\n\n")
302-
f.write("### Response\n\n")
303-
f.write(f"{single_result['response']}\n\n")
304-
305-
f.write("---\n\n")
306-
307-
f.write("## Approach 2: Certamen Framework Tournament\n\n")
308-
f.write(f"Champion: {tournament_result['champion_model']}\n\n")
309-
f.write(
310-
f"Duration: {tournament_result['duration_seconds']:.1f} seconds ({tournament_result['duration_seconds'] / 60:.1f} minutes)\n\n"
311-
)
312-
f.write(f"Total Cost: ${tournament_result['cost_actual']:.4f}\n\n")
313-
314-
# Cost breakdown by model
315-
if tournament_result.get("cost_by_model"):
316-
f.write("### Cost Breakdown by Model\n\n")
317-
for model_key, cost in tournament_result["cost_by_model"].items():
318-
f.write(f"- {model_key}: ${cost:.4f}\n")
319-
f.write("\n")
320-
321-
# Cost and time comparisons
322-
if single_model_results:
323-
cost_estimate = single_model_results[0]["cost_estimate"]
324-
cost_actual = tournament_result["cost_actual"]
325-
if cost_estimate > 0:
326-
cost_multiple = f"{cost_actual / cost_estimate:.1f}x"
327-
else:
328-
cost_multiple = "N/A (free/local baseline)"
329-
f.write(f"Cost Multiple vs Single Model: {cost_multiple}\n\n")
330-
331-
f.write(
332-
f"Time Multiple vs Single Model: {tournament_result['duration_seconds'] / single_model_results[0]['duration_seconds']:.1f}x\n\n"
333-
)
334-
335-
if tournament_result["eliminated_models"]:
336-
f.write(
337-
f"Eliminated Models: {', '.join(str(m) for m in tournament_result['eliminated_models'])}\n\n"
338-
)
339-
340-
f.write("### Champion Response\n\n")
341-
f.write(f"{tournament_result['response']}\n\n")
342-
343-
f.write("---\n\n")
344-
345-
model_names = [res["model"] for res in single_model_results] + [
346-
"Certamen Framework"
347-
]
348-
f.write(generate_manual_evaluation_template(model_names))
353+
await asyncio.to_thread(
354+
_write_micro_report,
355+
output_path,
356+
question,
357+
config_path,
358+
single_model_results,
359+
tournament_result,
360+
)
349361

350362
print(f"\n📄 Results saved to: {output_path}")
351363
print()

0 commit comments

Comments
 (0)