chuckfinca Claude Opus 4.6 (1M context) commited on
Commit
de5b38c
·
1 Parent(s): 1d31773

Add JSON API endpoint (/api/ask, /api/remaining) for custom chat UI

Browse files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. app.py +86 -2
app.py CHANGED
@@ -459,7 +459,91 @@ def build_app() -> gr.Blocks:
459
  return demo
460
 
461
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
462
  if __name__ == "__main__":
 
 
 
463
  WORKSPACE_DIR = load_workspace()
464
- app = build_app()
465
- app.launch(ssr_mode=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
459
  return demo
460
 
461
 
462
+ def ask_question(question: str) -> dict:
463
+ """Synchronous API for the custom chat UI."""
464
+ if not _check_and_increment():
465
+ return {
466
+ "answer": None,
467
+ "error": "daily_limit",
468
+ "remaining": 0,
469
+ "trace_html": "",
470
+ "stats": "",
471
+ }
472
+
473
+ if not MODEL:
474
+ return {"answer": None, "error": "LH_MODEL not set", "remaining": _remaining()}
475
+
476
+ scratch_dir = Path(tempfile.mkdtemp(prefix="lh-scratch-"))
477
+ system_prompt = build_system_prompt(base_prompt="", workspace=WORKSPACE_DIR)
478
+ messages: list[Message] = [
479
+ {"role": "system", "content": system_prompt},
480
+ {"role": "user", "content": question},
481
+ ]
482
+
483
+ start = time.monotonic()
484
+ agent_run = run_agent_loop(
485
+ model=MODEL,
486
+ messages=messages,
487
+ tools=TOOL_DEFINITIONS,
488
+ completion=litellm.completion,
489
+ workspace=WORKSPACE_DIR,
490
+ scratch_dir=scratch_dir,
491
+ sandbox_fn=e2b_run_python,
492
+ )
493
+
494
+ try:
495
+ for event in agent_run:
496
+ pass
497
+ except Exception as exc:
498
+ return {"answer": None, "error": str(exc), "remaining": _remaining()}
499
+
500
+ trace = agent_run.trace
501
+ trace.wall_time_s = round(time.monotonic() - start, 2)
502
+ result = {
503
+ "question": question,
504
+ "source": SOURCE,
505
+ "passed": True,
506
+ "assertions": {},
507
+ "trace": asdict(trace),
508
+ }
509
+ upload_trace(result)
510
+ trace_html = render_trace(result, max_chars=2000)
511
+
512
+ return {
513
+ "answer": trace.answer or "(no answer)",
514
+ "stats": format_stats(trace),
515
+ "trace_html": trace_html,
516
+ "remaining": _remaining(),
517
+ "error": None,
518
+ }
519
+
520
+
521
  if __name__ == "__main__":
522
+ import fastapi
523
+ from fastapi.middleware.cors import CORSMiddleware
524
+
525
  WORKSPACE_DIR = load_workspace()
526
+ demo = build_app()
527
+
528
+ # Mount the JSON API on the same FastAPI app
529
+ fastapi_app = fastapi.FastAPI()
530
+ fastapi_app.add_middleware(
531
+ CORSMiddleware,
532
+ allow_origins=["*"],
533
+ allow_methods=["POST"],
534
+ allow_headers=["*"],
535
+ )
536
+
537
+ @fastapi_app.post("/api/ask")
538
+ async def api_ask(request: fastapi.Request):
539
+ body = await request.json()
540
+ question = body.get("question", "")
541
+ if not question:
542
+ return {"error": "No question provided"}
543
+ return ask_question(question)
544
+
545
+ @fastapi_app.get("/api/remaining")
546
+ async def api_remaining():
547
+ return {"remaining": _remaining(), "limit": DAILY_LIMIT}
548
+
549
+ demo.launch(ssr_mode=False, app=fastapi_app)