crypto-copilot-public / app /ui_proxy.py
Eyob-Sol's picture
Update app/ui_proxy.py
47cca58 verified
Raw
History Blame Contribute Delete
4.55 kB
# app/ui_proxy.py
from __future__ import annotations
import json, os, time
from typing import Tuple, Generator
import gradio as gr
from gradio_client import Client
def _pretty(x) -> str:
try:
return json.dumps(x, indent=2, ensure_ascii=False)
except Exception:
return str(x)
def _normalize_space_id(raw: str) -> str:
"""Accept 'owner/repo' or a full URL; return 'owner/repo' only."""
s = (raw or "").strip()
for pref in ("https://huggingface.co/spaces/", "http://huggingface.co/spaces/"):
if s.startswith(pref):
s = s[len(pref):].strip("/ ")
return s
def _client() -> Client:
space_id = _normalize_space_id(os.getenv("PROXY_BACKEND_SPACE"))
if not space_id or "/" not in space_id:
raise RuntimeError("Backend is not configured.")
token = (os.getenv("HF_API_TOKEN") or os.getenv("HF_TOKEN") or "").strip()
if not token:
raise RuntimeError("Access to backend is not available.")
return Client(space_id, hf_token=token)
def _pick_api_name(cli: Client) -> str:
manual = (os.getenv("PROXY_API_NAME") or "").strip()
if manual:
return manual if manual.startswith("/") else f"/{manual}"
try:
info = cli.view_api()
if not info:
return "/predict"
eps = info.get("endpoints") or []
names = [e.get("name") for e in eps if e and e.get("name")]
if "/predict" in names:
return "/predict"
if names:
return names[0]
except Exception:
pass
return "/predict"
def handle_query_proxy(q: str) -> Generator[Tuple[str, str, str, str, str], None, None]:
"""Yields: routed_json, plan_json, answer_json, human_md, perf_json"""
loading = "### ⏳ Fetching…\nContacting the analysis backend."
try:
# Minimal, generic status
yield "{}", "{}", "{}", loading, _pretty({"status": "connecting"})
cli = _client()
api_name = _pick_api_name(cli)
yield "{}", "{}", "{}", loading, _pretty({"status": "running"})
job = cli.submit(q, api_name=api_name)
# Tiny delay for nicer UX
time.sleep(0.3)
if not job.done():
yield "{}", "{}", "{}", loading, _pretty({"status": "running"})
routed_json, plan_json, answer_json, human_text, perf_json = job.result()
yield routed_json, plan_json, answer_json, human_text, perf_json
except Exception:
friendly = (
"### ❌ Something went wrong\n"
"Please try again in a moment."
)
# No exception details, no env, no tokens.
yield "{}", "{}", "{}", friendly, _pretty({"status": "error"})
def build_proxy() -> gr.Blocks:
with gr.Blocks(title="Crypto Copilot (Public)") as demo:
gr.Markdown("## Crypto Copilot — Public UI")
with gr.Row():
query = gr.Textbox(
label="Your question",
placeholder="Examples: 'What is BTC price now?', 'Compare BTC vs ETH 30d drawdown'",
lines=2,
)
run_btn = gr.Button("Run", variant="primary")
# Summary first (shows loading message immediately)
with gr.Tab("Summary"):
human_out = gr.Markdown()
with gr.Tab("How it was decided"):
routed_out = gr.Code(language="json", label="Router output (intent/assets/windows)")
with gr.Tab("Plan"):
plan_out = gr.Code(language="json", label="Planner output")
with gr.Tab("Answer JSON"):
answer_out = gr.Code(language="json", label="Structured answer (schema)")
with gr.Tab("Perf & Cost"):
perf_out = gr.Code(language="json", label="Latency, backend, cache stats")
run_btn.click(
fn=handle_query_proxy,
inputs=[query],
outputs=[routed_out, plan_out, answer_out, human_out, perf_out],
show_progress=True,
)
gr.Examples(
examples=[
"What is BTC price now?",
"Compare BTC vs ETH YTD and 30d drawdown",
"Show 7d return and volatility for SOL",
"Portfolio: BTC 60% ETH 40%",
"What moved ETH today?",
],
inputs=[query],
outputs=[routed_out, plan_out, answer_out, human_out, perf_out],
fn=handle_query_proxy,
cache_examples=False,
label="Try these example questions",
run_on_click=True,
)
return demo