"""AutoSource dashboard โ€” AG-UI-aligned event stream viewer + human approval gate. Modes: Live follow โ€” follows a run being written right now (start one from the sidebar). Replay โ€” plays any recorded run from traces/ turn by turn (zero API calls). Tabs: ๐Ÿ“Š Mission control โ€” the animated run view + AP2 approval gate. ๐Ÿ”ฌ Under the hood โ€” agent system prompts, parsed transcripts, raw events, and what every module contributes. Run: streamlit run ui/app.py """ from __future__ import annotations import html import json import os import subprocess import sys import time import uuid from datetime import datetime from pathlib import Path import streamlit as st sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from core.config import PROJECT_ROOT, api_key, db_path, settings, traces_dir # noqa: E402 from core.hosted_runtime import ( # noqa: E402 acquire_live_slot, cleanup_expired_sessions, get_workspace, is_hosted, live_slot_status, new_session_id, release_live_slot, ) st.set_page_config(page_title="AutoSource", page_icon="๐Ÿค", layout="wide") # ---------------------------------------------------------------- css / anims # All custom components use EXPLICIT text colors on light backgrounds so they # stay readable in both Streamlit themes (dark theme made inherited text white). st.markdown(""" """, unsafe_allow_html=True) # ---------------------------------------------------------------- data access HOSTED = is_hosted() BUNDLED_TRACES = traces_dir() if HOSTED: if "runtime_session_id" not in st.session_state: st.session_state.runtime_session_id = new_session_id() cleanup_expired_sessions() WORKSPACE = get_workspace(st.session_state.runtime_session_id) LIVE_TRACES = WORKSPACE.traces LIVE_DB = WORKSPACE.db CHILD_ENV = WORKSPACE.child_env() else: WORKSPACE = None LIVE_TRACES = BUNDLED_TRACES LIVE_DB = db_path() CHILD_ENV = os.environ.copy() def reset_warehouse() -> subprocess.CompletedProcess: """Seed this visitor's warehouse without affecting any other session.""" return subprocess.run( [sys.executable, "-m", "scripts.init_db"], cwd=str(PROJECT_ROOT), env=CHILD_ENV, capture_output=True, text=True, timeout=30, ) if HOSTED and not LIVE_DB.exists(): init_result = reset_warehouse() if init_result.returncode != 0: st.error("Could not initialize your private demo warehouse. Please reload.\n\n" + (init_result.stderr or init_result.stdout)[-1200:]) st.stop() def list_runs() -> list[str]: paths = list(LIVE_TRACES.glob("*.jsonl")) if LIVE_TRACES != BUNDLED_TRACES: paths.extend(BUNDLED_TRACES.glob("*.jsonl")) by_id = {p.stem: p for p in paths if not p.stem.startswith(".") and p.stem != "llm_calls"} return sorted(by_id, key=lambda r: by_id[r].stat().st_mtime, reverse=True) def trace_root_for_run(run_id: str) -> Path: if (LIVE_TRACES / f"{run_id}.jsonl").exists(): return LIVE_TRACES return BUNDLED_TRACES def load_events(run_id: str) -> list[dict]: path = trace_root_for_run(run_id) / f"{run_id}.jsonl" if not path.exists(): return [] events = [] for line in path.read_text(encoding="utf-8").splitlines(): line = line.strip() if line: try: events.append(json.loads(line)) except json.JSONDecodeError: pass # tail of a line still being written return events def load_mandates(run_id: str) -> dict | None: path = trace_root_for_run(run_id) / f"{run_id}.mandates.json" if path.exists(): try: return json.loads(path.read_text(encoding="utf-8")) except json.JSONDecodeError: return None return None def write_decision(run_id: str, payload: dict) -> None: if not (LIVE_TRACES / f"{run_id}.jsonl").exists(): raise PermissionError("recorded replay runs are read-only") tmp = LIVE_TRACES / f".{run_id}.decision.tmp" tmp.write_text(json.dumps(payload), encoding="utf-8") tmp.replace(LIVE_TRACES / f"{run_id}.decision.json") def start_live_run(sku: str | None) -> str: """Launch scripts.run_demo as a detached subprocess; returns its run_id.""" session_tag = (st.session_state.runtime_session_id[:8] if HOSTED else "local") run_id = ("live-" + session_tag + "-" + datetime.now().strftime("%Y%m%d-%H%M%S") + "-" + uuid.uuid4().hex[:4]) args = [sys.executable, "-u", "-m", "scripts.run_demo", "--run-id", run_id] if sku: args += ["--sku", sku] token = None if HOSTED: token = acquire_live_slot(st.session_state.runtime_session_id, run_id) if token is None: raise RuntimeError("another visitor is currently negotiating") args += ["--live-lock-token", token] console = open(LIVE_TRACES / f"{run_id}.console.log", "w", encoding="utf-8") try: subprocess.Popen(args, cwd=str(PROJECT_ROOT), env=CHILD_ENV, stdout=console, stderr=subprocess.STDOUT) console.close() except Exception: console.close() if token: release_live_slot(token) raise return run_id def pending_gate_exists() -> bool: for path in LIVE_TRACES.glob("*.mandates.json"): try: if json.loads(path.read_text(encoding="utf-8"))["context"]["status"] == "pending": return True except (KeyError, json.JSONDecodeError, OSError): continue return False # ---------------------------------------------------------------- sidebar st.sidebar.title("๐Ÿค AutoSource") st.sidebar.caption("Autonomous procurement & negotiation network") qp = st.query_params runs = list_runs() default_mode = 0 if qp.get("mode", "live") == "live" else 1 mode = st.sidebar.radio("Mode", ["Live follow", "Replay"], index=default_mode) requested_run = qp.get("run") if requested_run and runs and requested_run not in runs: if HOSTED: own_prefix = f"live-{st.session_state.runtime_session_id[:8]}-" pending_console = LIVE_TRACES / f"{requested_run}.console.log" if requested_run.startswith(own_prefix) and pending_console.exists(): st.sidebar.info(f"โณ starting {requested_run} โ€ฆ") time.sleep(2) st.rerun() else: st.sidebar.warning("That run belongs to an expired or different " "browser session. Visitor runs are private.") del st.query_params["run"] st.rerun() else: st.sidebar.info(f"โณ waiting for {requested_run} โ€ฆ") if st.sidebar.button("Cancel waiting"): del st.query_params["run"] st.rerun() time.sleep(2) st.rerun() if not runs: st.info("No runs recorded yet. Start one below or run: " "`python -m scripts.run_demo`") run_id = None else: default_run = requested_run if requested_run in runs else runs[0] run_id = st.sidebar.selectbox("Run", runs, index=runs.index(default_run)) # ---- live demo controls --------------------------------------------------- st.sidebar.divider() st.sidebar.subheader("๐ŸŽฎ Live demo") if HOSTED: st.sidebar.caption("Private visitor session ยท " f"`{st.session_state.runtime_session_id[:8]}`") if not api_key("GROQ_API_KEY"): st.sidebar.warning("Live mode is temporarily unavailable because the Space " "owner has not configured `GROQ_API_KEY`. Replay still works.") else: sku_options = (["SEED-MAIZE-01", "FERT-NPK-01", "(auto: first low-stock item)"] if HOSTED else ["(auto: first low-stock item)", "SEED-MAIZE-01", "FERT-NPK-01"]) sku_choice = st.sidebar.selectbox("Item to restock", sku_options) if HOSTED and sku_choice == "FERT-NPK-01": st.sidebar.caption("Stress-test item: vendor floors exceed its budget, so " "a safe walk-away is the expected result.") hosted_cfg = settings().get("hosted", {}) launches = int(st.session_state.get("live_launches", 0)) max_launches = int(hosted_cfg.get("max_runs_per_session", 3)) last_launch = float(st.session_state.get("last_live_launch", 0)) cooldown_left = max(0, int(hosted_cfg.get("cooldown_s", 120) - (time.time() - last_launch))) slot = live_slot_status() if HOSTED else None at_limit = HOSTED and launches >= max_launches has_pending_gate = pending_gate_exists() disabled_reason = None if has_pending_gate: disabled_reason = "Approve or reject your pending mandate before another run." elif slot: disabled_reason = "Another live negotiation is using the shared free-tier slot." elif cooldown_left > 0: disabled_reason = f"This session can launch again in {cooldown_left}s." elif at_limit: disabled_reason = (f"This browser session used its {max_launches} live runs. " "Replay remains available.") if disabled_reason: st.sidebar.info(disabled_reason) if st.sidebar.button("๐Ÿš€ Trigger stock check & negotiate", type="primary", use_container_width=True, disabled=bool(disabled_reason)): sku = None if sku_choice.startswith("(") else sku_choice try: new_run = start_live_run(sku) except Exception as exc: # noqa: BLE001 st.sidebar.error(f"Could not start: {str(exc)[:240]}") else: st.session_state.live_launches = launches + 1 st.session_state.last_live_launch = time.time() st.query_params["run"] = new_run st.query_params["mode"] = "live" st.sidebar.success(f"started {new_run}") time.sleep(2) st.rerun() reset_blocked = bool(slot) or has_pending_gate if st.sidebar.button("๐Ÿ”„ Reset warehouse (seed data)", use_container_width=True, disabled=reset_blocked): out = reset_warehouse() st.sidebar.success("warehouse reset" if out.returncode == 0 else f"failed: {out.stdout}{out.stderr}") if reset_blocked: st.sidebar.caption("Finish the active negotiation/approval before resetting.") elif HOSTED: st.sidebar.caption(f"{launches}/{max_launches} live runs used in this session. " "Data expires automatically after 6 hours.") else: st.sidebar.caption("Live runs negotiate on real free-tier APIs โ€” see " "docs/LIVE_DEMO.md") events_all = load_events(run_id) if run_id else [] TRACES = trace_root_for_run(run_id) if run_id else LIVE_TRACES run_is_owned = bool(run_id and (LIVE_TRACES / f"{run_id}.jsonl").exists()) if mode == "Replay" and run_id: st.sidebar.divider() speed = st.sidebar.slider("Replay speed (events/tick)", 1, 10, 2) if "replay_idx" not in st.session_state or st.session_state.get("replay_run") != run_id: st.session_state.replay_idx = 0 st.session_state.replay_run = run_id st.session_state.playing = False c1, c2, c3 = st.sidebar.columns(3) if c1.button("โ–ถ Play"): st.session_state.playing = True if c2.button("โธ Pause"): st.session_state.playing = False if c3.button("โฎ Reset"): st.session_state.replay_idx = 0 st.session_state.playing = False st.session_state.replay_idx = st.sidebar.slider( "Position", 0, max(len(events_all), 1), st.session_state.replay_idx) events = events_all[:st.session_state.replay_idx] else: events = events_all if run_id: st.sidebar.caption(f"{len(events)} events ยท auto-refreshing") if run_id is None: st.stop() # ---------------------------------------------------------------- derive state AGENTS = [("auditor", "๐Ÿ” Auditor"), ("sourcing", "๐Ÿ“‡ Sourcing"), ("buyer", "๐Ÿค Buyer"), ("vendor", "๐Ÿช Vendors"), ("settlement", "โš–๏ธ Settlement")] def agent_key(actor: str) -> str: return "vendor" if actor.startswith("vendor") else actor last_status: dict[str, str] = {} for e in events: k = agent_key(e["actor"]) if e["type"] == "STATE_UPDATE" and "status" in e["data"]: last_status[k] = str(e["data"]["status"]).replace("_", " ") elif e["type"] == "TEXT_MESSAGE": last_status[k] = "speakingโ€ฆ" active = agent_key(events[-1]["actor"]) if events else None pipeline_running = bool(events) and not any( e["type"] == "STATE_UPDATE" and e["data"].get("status") in ("complete", "rejected", "pipeline_done", "approval_timeout") for e in events[-6:]) meta = {"budget": None, "best_ask": None, "round": None, "vendor": None} deals, winner, requests_seen, active_sku, transcript_refs = [], None, [], None, [] for e in events: d = e["data"] if e["type"] == "STATE_UPDATE": if "buyer_offer" in d: meta["round"], meta["best_ask"] = d.get("round"), d.get("vendor_ask") meta["vendor"] = d.get("vendor") if "winner" in d and d.get("status") == "pipeline_done": winner = d["winner"] if "shortlist" in d: meta["budget"] = d["shortlist"].get("max_unit_budget") active_sku = d["shortlist"].get("sku") if d.get("status") == "negotiation_done": deals.append(d) if d.get("transcript_ref"): transcript_refs.append(d["transcript_ref"]) if e["type"] == "TEXT_MESSAGE" and "request" in d: requests_seen.append(d["request"]) request = next((r for r in requests_seen if r["sku"] == active_sku), requests_seen[0] if requests_seen else None) # ---------------------------------------------------------------- tabs tab_main, tab_tech = st.tabs(["๐Ÿ“Š Mission control", "๐Ÿ”ฌ Under the hood"]) with tab_main: cols = st.columns(len(AGENTS)) for col, (key, label) in zip(cols, AGENTS): is_active = key == active and (mode == "Replay" or pipeline_running) col.markdown( f"""
{label}
{last_status.get(key, 'idle')}
""", unsafe_allow_html=True) st.divider() m1, m2, m3, m4, m5 = st.columns(5) list_price = request["list_price"] if request else None m1.metric("Item", request["sku"] if request else "โ€”") m2.metric("List price", f"{list_price}" if list_price else "โ€”") m3.metric("Hidden budget ๐Ÿคซ", f"{request['max_unit_budget']}" if request else "โ€”") if winner and winner.get("agreed_unit_price") and list_price: saved = round((list_price - winner["agreed_unit_price"]) / list_price * 100, 1) m4.metric("Winning price", winner["agreed_unit_price"], delta=f"-{saved}% vs list", delta_color="inverse") else: m4.metric("Current ask", meta["best_ask"] or "โ€”") m5.metric("Deals settled", f"{sum(1 for d in deals if d.get('settled'))}/{len(deals)}" if deals else "โ€”") left, right = st.columns([3, 2]) with left: st.subheader("Negotiation timeline") box = st.container(height=520) with box: for e in events: d, actor = e["data"], e["actor"] if e["type"] == "TEXT_MESSAGE": cls = ("buyer" if actor == "buyer" else "vendor" if actor.startswith("vendor") else "human" if actor == "human" else "system") price = f'@{d["price"]}' \ if d.get("price") is not None else "" flags = "".join( f'๐Ÿ›ก {html.escape(str(f))}' for f in d.get("flags", []) if f.startswith(("BLOCKED", "clamped", "non_monotonic", "premature"))) who = html.escape(actor.upper()) if d.get("round"): who += f" ยท R{d['round']}" st.markdown( f'
{who}
' f'{html.escape(str(d["text"]))}{price}{flags}
', unsafe_allow_html=True) elif e["type"] == "TOOL_CALL": st.markdown(f'๐Ÿ”ง {html.escape(actor)} โ†’ MCP ' f'{html.escape(str(d["tool"]))}()', unsafe_allow_html=True) with right: st.subheader("Deals") if deals: rows = [{"vendor": d.get("vendor_id"), "outcome": d.get("outcome"), "price": d.get("agreed_unit_price"), "rounds": d.get("rounds")} for d in deals] st.dataframe(rows, use_container_width=True, hide_index=True) else: st.caption("No negotiations finished yet.") if winner: st.markdown( f'
๐Ÿ† Winner: {winner["vendor_id"]} @ ' f'{winner["agreed_unit_price"]}/unit ยท total ' f'{winner["landed_cost"]}
', unsafe_allow_html=True) mandates = load_mandates(run_id) if mandates: ctx = mandates["context"] st.subheader("AP2 mandate") if ctx["status"] == "pending": st.warning( f"**Approval required** โ€” {ctx['vendor_name']} ยท " f"{mandates['cart']['qty']} ร— {mandates['cart']['sku']} @ " f"{mandates['cart']['unit_price']} = " f"**{mandates['cart']['total']}** " f"(saves {ctx['savings_pct']}% vs list)") with st.expander("Intent ยท Cart ยท Payment (AP2 trio)"): st.json(mandates["intent"]) st.json(mandates["cart"]) st.json(mandates["payment"]) if run_is_owned: b1, b2 = st.columns(2) if b1.button("โœ… Approve", type="primary", use_container_width=True): write_decision(run_id, {"decision": "approve"}) st.toast("Approved โ€” signing cart mandate") time.sleep(1) st.rerun() if b2.button("โŒ Reject", use_container_width=True): write_decision(run_id, {"decision": "reject"}) st.toast("Rejected") time.sleep(1) st.rerun() new_price = st.number_input( "โ€ฆor change max unit price", value=float(mandates["intent"]["max_unit_price"]), step=0.05, format="%.2f") if st.button("๐Ÿ’ฐ Change budget & re-evaluate", use_container_width=True): write_decision(run_id, {"decision": "change_budget", "new_max_unit_price": new_price}) st.rerun() else: st.info("This is a read-only recorded run. Start a new live run " "to make an approval decision.") elif ctx["status"] == "approved": po = ctx.get("po", {}) st.success(f"โœ… PO **{po.get('po_id', 'โ€”')}** written via MCP ยท " f"total {po.get('total', 'โ€”')} ยท MOCK payment " f"authorized. Saved {ctx['savings_pct']}% vs list price.") st.balloons() elif ctx["status"] == "rejected": st.error("โŒ Purchase rejected by the human. Nothing was written.") # ---------------------------------------------------------------- tech tab with tab_tech: sub_arch, sub_llm, sub_console, sub_events, sub_db, sub_health = st.tabs( ["๐Ÿงญ Architecture & prompts", "๐Ÿง  LLM calls", "๐Ÿ–ฅ Console & files", "๐Ÿ“ก Events & transcripts", "๐Ÿ—„ Warehouse DB", "๐Ÿฉบ Health & config"]) # ---------------- architecture + prompts ---------------- with sub_arch: st.markdown("### How AutoSource fits together") st.markdown(""" | Stage | Module | What it contributes | |---|---|---| | ๐Ÿ—„ Warehouse | `mcp_server/server.py` | FastMCP server โ€” the **only** code that touches SQLite. 5 tools; `cost_floor` never leaves it | | ๐Ÿ” Detect | `agents/auditor/auditor.py` | Deterministic shortage scan over MCP โ†’ `ProcurementRequest` (budget = list ร— 0.9) | | ๐Ÿ“ก Hand-off | `core/a2a_layer.py` | A2A protocol: Agent Cards + JSON-RPC `message/send` over HTTP (official a2a-sdk types) | | ๐Ÿ“‡ Rank | `agents/sourcing/` | Deterministic vendor score + Gemini re-rank + `vendor_memory` learning | | ๐Ÿค Negotiate | `core/negotiation.py` | The referee: state machine, **hard validators** (budget/floor/monotonicity), transcripts | | ๐Ÿ—ฃ Speak | `agents/buyer/` ยท `agents/vendor/` | LLM negotiators with **hidden thresholds** in system prompts (below) | | โš–๏ธ Settle | `agents/settlement/` | Deterministic winner (landed cost โ†’ reliability โ†’ lead time) + memory write-back | | ๐Ÿ” Gate | `protocols/ap2/` + `orchestrator/gate.py` | Intentโ†’Cartโ†’Payment mandates, hash-bound; human approve โ†’ verify โ†’ PO | | ๐Ÿšฆ Router | `core/router.py` | Token-bucket RPM limit, backoff on 429, Groq key rotation, provider failover | | ๐Ÿ“ˆ Prove | `eval/` | 20+ scenario harness, savings chart, LLM-judge; asserts 0 violations | """) st.markdown("### Agent system prompts (the real ones)") st.caption("Live imports from the agent source โ€” what the models actually " "see. The CONFIDENTIAL lines are the hidden thresholds; " "deterministic validators enforce them even if a model ignores " "its prompt.") try: from agents.buyer.buyer import SYSTEM_TMPL as BUYER_TMPL from agents.vendor.vendor import PERSONA_TACTICS from agents.vendor.vendor import SYSTEM_TMPL as VENDOR_TMPL with st.expander("๐Ÿค Buyer โ€” 'shrewd but fair procurement officer'"): st.code(BUYER_TMPL, language="text") with st.expander("๐Ÿช Vendor โ€” persona-driven seller"): st.code(VENDOR_TMPL, language="text") st.markdown("**Persona tactics injected per vendor:**") for persona, tactics in PERSONA_TACTICS.items(): st.markdown(f"- **{persona}** โ€” {tactics}") except ImportError: st.info("Agent sources not bundled in this deployment.") try: from agents.sourcing.ranking import RERANK_SYSTEM, WEIGHTS with st.expander("๐Ÿ“‡ Sourcing โ€” deterministic score + LLM re-rank"): st.markdown(f"**Score weights (code):** `{WEIGHTS}`") st.code(RERANK_SYSTEM, language="text") except ImportError: pass try: from eval.judge import SYSTEM as JUDGE_SYSTEM with st.expander("โš–๏ธ LLM-judge โ€” transcript auditor"): st.code(JUDGE_SYSTEM, language="text") except ImportError: pass # ---------------- LLM call log ---------------- with sub_llm: st.markdown("### Every LLM call the router made") st.caption("Full system prompt, user message, and raw response per call โ€” " "logged by `core/router.py` to `traces/llm_calls.jsonl`. " "Failures (429s, failovers) appear in red.") call_log = TRACES / "llm_calls.jsonl" calls: list[dict] = [] if call_log.exists(): for line in call_log.read_text(encoding="utf-8").splitlines(): try: calls.append(json.loads(line)) except json.JSONDecodeError: pass # scope to this run's time window when possible if events and calls: t_start, t_end = events[0]["ts"] - 5, events[-1]["ts"] + 90 run_calls = [c for c in calls if t_start <= c.get("ts", 0) <= t_end] else: run_calls = calls c1, c2, c3 = st.columns(3) scope = c1.radio("Scope", ["This run's window", "All calls"], horizontal=True) shown_calls = run_calls if scope == "This run's window" else calls roles = sorted({c.get("role", "?") for c in shown_calls}) role_pick = c2.multiselect("Agent role", roles, default=roles) only_fail = c3.checkbox("Failures only") shown_calls = [c for c in shown_calls if c.get("role") in role_pick and (not only_fail or not c.get("ok", True))] st.caption(f"{len(shown_calls)} calls shown (log has {len(calls)} total)") for i, c in enumerate(reversed(shown_calls[-80:])): ok = c.get("ok", True) ts = datetime.fromtimestamp(c["ts"]).strftime("%H:%M:%S") if c.get("ts") else "โ€”" head = (f'{"โœ…" if ok else "โŒ"} {ts} ยท {c.get("role")} โ†’ ' f'{c.get("provider")} ยท {c.get("latency_ms", "?")} ms' + ("" if ok else " ยท FAILED")) with st.expander(head): st.markdown("**System prompt**") st.code(c.get("system", ""), language="text") st.markdown("**User message**") st.code(json.dumps(c.get("messages", []), indent=2, ensure_ascii=False), language="json") st.markdown("**Response**" if ok else "**Error**") st.code(c.get("response", c.get("error", "")), language="text") # ---------------- console + file browser ---------------- with sub_console: st.markdown("### Run console (subprocess stdout/stderr)") console_path = TRACES / f"{run_id}.console.log" if console_path.exists(): lines = console_path.read_text(encoding="utf-8", errors="replace").splitlines() body = "\n".join(lines[-120:]) or "(empty)" st.markdown(f'
{html.escape(body)}
', unsafe_allow_html=True) st.caption(f"last 120 of {len(lines)} lines ยท traces/{run_id}.console.log") else: st.info("No console capture for this run โ€” console logs exist only for " "runs started from the ๐ŸŽฎ Live demo button. Terminal-started " "runs print to your terminal.") st.markdown("### Trace file browser") files = sorted(TRACES.rglob("*"), key=lambda p: p.stat().st_mtime, reverse=True) files = [f for f in files if f.is_file() and not f.name.startswith(".")] names = [str(f.relative_to(TRACES)) for f in files[:60]] pick = st.selectbox("File", names) if names else None if pick: fpath = TRACES / pick size_kb = fpath.stat().st_size / 1024 st.caption(f"{size_kb:.1f} KB ยท modified " f"{datetime.fromtimestamp(fpath.stat().st_mtime):%H:%M:%S}") raw = fpath.read_text(encoding="utf-8", errors="replace") if fpath.suffix == ".json": try: st.json(json.loads(raw)) except json.JSONDecodeError: st.code(raw[-8000:], language="text") else: tail = "\n".join(raw.splitlines()[-150:]) st.markdown(f'
{html.escape(tail)}
', unsafe_allow_html=True) # ---------------- events + transcripts ---------------- with sub_events: st.markdown("### Negotiation transcripts (parsed)") st.caption("Each turn stores the raw message AND the parsed price, so " "fabricated numbers can be caught. ๐Ÿ›ก flags = validator actions.") if transcript_refs: for ref in transcript_refs: ref_path = Path(ref) tpath = (ref_path if ref_path.is_absolute() else TRACES.parent / ref_path) if not tpath.exists(): tpath = TRACES / "negotiations" / ref_path.name if not tpath.exists(): continue t = json.loads(tpath.read_text(encoding="utf-8")) m, res = t["meta"], t["result"] label = (f"{m['vendor_id']} โ€” {res['outcome']} " f"{'@ ' + str(res['agreed_unit_price']) if res['agreed_unit_price'] else ''} " f"(budget {m['budget']} ยท floor {m['floor']} ๐Ÿคซ)") with st.expander(label): st.dataframe( [{"actor": turn["actor"], "action": turn["action"], "price": turn["price"], "message": turn["raw"], "๐Ÿ›ก flags": ", ".join(turn["flags"])} for turn in t["turns"]], use_container_width=True, hide_index=True) else: st.caption("No transcripts in this run yet.") st.markdown("### Raw AG-UI event stream") type_filter = st.multiselect( "Event types", ["TEXT_MESSAGE", "STATE_UPDATE", "TOOL_CALL", "UI_COMPONENT"], default=["TOOL_CALL", "UI_COMPONENT"]) shown = [e for e in events if e["type"] in type_filter] st.caption(f"{len(shown)} of {len(events)} events (traces/{run_id}.jsonl)") for e in shown[-60:]: with st.expander( f'{e["type"]} ยท {e["actor"]} ยท {e.get("iso_ts", "")[11:19]}'): st.json(e["data"]) st.markdown("### Router telemetry (from this run)") stats_events = [e["data"]["router_stats"] for e in events if e["type"] == "STATE_UPDATE" and "router_stats" in e["data"]] if stats_events: s = stats_events[-1] r1, r2, r3, r4 = st.columns(4) r1.metric("LLM calls", s.get("calls", 0)) r2.metric("Tokens", f'{s.get("prompt_tokens", 0) + s.get("completion_tokens", 0):,}') r3.metric("Failovers", s.get("failovers", 0)) r4.metric("Providers", ", ".join(s.get("by_provider", {}).keys()) or "โ€”") if s.get("last_errors"): st.error("Last provider errors: " + json.dumps(s["last_errors"], indent=2)) else: st.caption("No router telemetry events in this run " "(recorded before this feature, or no LLM calls yet).") # ---------------- warehouse DB inspector ---------------- with sub_db: st.markdown("### Warehouse DB (read-only debug view)") st.caption("โš  Debug panel only. Agents NEVER touch this DB directly โ€” " "they go through the MCP server's five tools. This inspector " "reads the same SQLite file so you can verify what the tools did.") try: import sqlite3 if LIVE_DB.exists(): conn = sqlite3.connect(f"file:{LIVE_DB}?mode=ro", uri=True) for table in ("inventory", "purchase_orders", "vendor_memory", "suppliers"): with st.expander(f"table: {table}", expanded=(table == "purchase_orders")): import pandas as pd df = pd.read_sql_query(f"SELECT * FROM {table}", conn) # noqa: S608 โ€” fixed table names if table == "suppliers": df = df.drop(columns=["cost_floor"]) st.caption("cost_floor hidden โ€” same rule as the MCP " "server: buyers must never see it.") st.dataframe(df, use_container_width=True, hide_index=True) conn.close() else: st.info("Your private warehouse has not been initialized yet.") except Exception as e: # noqa: BLE001 st.warning(f"DB inspector unavailable: {e}") # ---------------- health & config ---------------- with sub_health: st.markdown("### API keys (presence only โ€” values never shown)") for key_name in ("GROQ_API_KEY", "GROQ_API_KEY_2", "GEMINI_API_KEY", "OPENROUTER_API_KEY", "HF_TOKEN"): st.markdown(f"- `{key_name}`: " + ("โœ… set" if api_key(key_name) else "โŒ missing")) if api_key("GROQ_API_KEY") and not HOSTED: st.markdown("### Provider health check") st.caption("Sends one tiny live request per provider.") if st.button("๐Ÿฉบ Test providers now"): from core.router import _PROVIDER_CLASSES for pname, cls in _PROVIDER_CLASSES.items(): try: provider = cls() provider.chat("Reply with JSON only.", [{"role": "user", "content": 'reply with {"ok": true}'}], 0.1, True) st.success(f"{pname}: OK") except Exception as e: # noqa: BLE001 st.error(f"{pname}: {str(e)[:220]}") elif HOSTED: st.caption("Live provider pings are disabled on the public Space to " "protect the shared free-tier quota. Pipeline calls still " "report provider health in Router telemetry.") st.markdown("### Runtime configuration (config/settings.yaml)") cfg_path = PROJECT_ROOT / "config" / "settings.yaml" if cfg_path.exists(): st.code(cfg_path.read_text(encoding="utf-8"), language="yaml") # ---------------------------------------------------------------- refresh loop if mode == "Live follow" and pipeline_running: time.sleep(2) st.rerun() elif mode == "Replay" and st.session_state.get("playing"): if st.session_state.replay_idx < len(events_all): st.session_state.replay_idx = min(len(events_all), st.session_state.replay_idx + speed) time.sleep(0.5) st.rerun() else: st.session_state.playing = False