simplifying ui

This commit is contained in:
Ahmed Burney 2026-01-16 22:55:35 +05:00
parent eb7d7be76c
commit 43799ded85

View file

@ -1,13 +1,11 @@
import json
import os
from datetime import datetime
import httpx
import streamlit as st
# Configuration
PLANO_ENDPOINT = os.getenv("PLANO_ENDPOINT", "http://localhost:8001/v1")
CASE_SERVICE_URL = "http://localhost:10540"
st.set_page_config(
page_title="Credit Risk Case Copilot",
@ -18,7 +16,7 @@ st.set_page_config(
# Load scenarios
def load_scenario(scenario_file):
def load_scenario(scenario_file: str):
"""Load scenario JSON from file."""
try:
with open(scenario_file, "r") as f:
@ -30,13 +28,15 @@ def load_scenario(scenario_file):
# Initialize session state
if "assessment_result" not in st.session_state:
st.session_state.assessment_result = None
if "case_id" not in st.session_state:
st.session_state.case_id = None
if "raw_result" not in st.session_state:
st.session_state.raw_result = None
if "application_json" not in st.session_state:
st.session_state.application_json = "{}"
# Header
st.title("🏦 Credit Risk Case Copilot")
st.markdown("**AI-Powered Credit Risk Assessment & Case Management**")
st.markdown("A minimal UI for the Plano + CrewAI credit risk demo.")
st.divider()
# Sidebar
@ -67,38 +67,45 @@ with st.sidebar:
# JSON input area
application_json = st.text_area(
"Loan Application JSON",
value=st.session_state.get("application_json", "{}"),
height=400,
value=st.session_state.application_json,
height=380,
help="Paste or edit loan application JSON",
)
# Assess button
if st.button("🔍 Assess Risk", type="primary", use_container_width=True):
try:
# Parse JSON
application_data = json.loads(application_json)
col_a, col_b = st.columns(2)
# Call Plano orchestrator
with st.spinner("Running risk assessment..."):
response = httpx.post(
f"{PLANO_ENDPOINT}/chat/completions",
json={
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": f"Assess credit risk for this loan application:\n\n{json.dumps(application_data, indent=2)}",
}
],
},
timeout=60.0,
)
with col_a:
if st.button("🔍 Assess Risk", type="primary", use_container_width=True):
try:
application_data = json.loads(application_json)
with st.spinner("Running risk assessment..."):
response = httpx.post(
f"{PLANO_ENDPOINT}/chat/completions",
json={
# Use risk_reasoning if youre standardizing on aliases.
# If you want plain OpenAI model routing, set "gpt-4o".
"model": "risk_reasoning",
"messages": [
{
"role": "user",
"content": (
"Assess credit risk for this loan application:\n\n"
f"{json.dumps(application_data, indent=2)}"
),
}
],
},
timeout=60.0,
)
if response.status_code == 200:
result = response.json()
content = result["choices"][0]["message"]["content"]
raw = response.json()
st.session_state.raw_result = raw
# Extract JSON from response
content = raw["choices"][0]["message"]["content"]
# Extract JSON block from response
if "```json" in content:
json_start = content.index("```json") + 7
json_end = content.index("```", json_start)
@ -107,160 +114,90 @@ with st.sidebar:
st.session_state.assessment_result = assessment
st.success("✅ Risk assessment complete!")
else:
st.error("Could not parse assessment result")
st.session_state.assessment_result = None
st.error(
"Could not parse JSON assessment from the agent response."
)
else:
st.session_state.assessment_result = None
st.session_state.raw_result = {
"status_code": response.status_code,
"text": response.text,
}
st.error(f"Error: {response.status_code} - {response.text}")
except json.JSONDecodeError:
st.error("Invalid JSON format")
except Exception as e:
st.error(f"Error: {str(e)}")
except json.JSONDecodeError:
st.error("Invalid JSON format")
except Exception as e:
st.error(f"Error: {str(e)}")
with col_b:
if st.button("🧹 Clear", use_container_width=True):
st.session_state.assessment_result = None
st.session_state.raw_result = None
st.session_state.application_json = "{}"
st.rerun()
# Main content area
if st.session_state.assessment_result:
result = st.session_state.assessment_result
# Risk summary
st.header("Risk Assessment Summary")
st.header("Decision")
col1, col2, col3, col4 = st.columns(4)
col1, col2, col3 = st.columns(3)
with col1:
risk_color = {"LOW": "🟢", "MEDIUM": "🟡", "HIGH": "🔴"}
st.metric(
"Risk Band",
f"{risk_color.get(result['risk_band'], '')} {result['risk_band']}",
)
risk_band = result.get("risk_band", "UNKNOWN")
st.metric("Risk Band", f"{risk_color.get(risk_band, '')} {risk_band}")
with col2:
st.metric("Confidence", f"{result['confidence']:.1%}")
confidence = result.get("confidence", 0.0)
try:
st.metric("Confidence", f"{float(confidence):.0%}")
except Exception:
st.metric("Confidence", str(confidence))
with col3:
st.metric("Recommended Action", result["recommended_action"])
with col4:
st.metric("Documents Required", len(result.get("required_documents", [])))
st.metric("Recommended Action", result.get("recommended_action", "REVIEW"))
st.divider()
# Tabbed interface
tab1, tab2, tab3, tab4, tab5 = st.tabs(
[
"📊 Risk Summary",
"🎯 Risk Drivers",
"📋 Policy & Compliance",
"📝 Decision Memo",
"🔍 Audit Trail",
]
)
tab1, tab2, tab3 = st.tabs(["🧾 Summary", "📝 Decision Memo", "🧪 Raw Output"])
with tab1:
st.subheader("Summary")
human = result.get("human_response", "")
if human:
st.write(human.split("```")[0].strip())
else:
st.info("No human-readable summary available.")
st.divider()
st.subheader("Normalized Application")
st.json(result.get("normalized_application", {}))
st.subheader("Assessment Overview")
st.write(result.get("human_response", "").split("```")[0])
with tab2:
st.subheader("Risk Drivers")
drivers = result.get("drivers", [])
for driver in drivers:
impact_color = {"CRITICAL": "🔴", "HIGH": "🟠", "MEDIUM": "🟡", "LOW": "🟢"}
st.markdown(
f"**{impact_color.get(driver['impact'], '')} {driver['factor']}** ({driver['impact']})"
)
st.write(driver["evidence"])
st.divider()
st.subheader("Decision Memo")
memo = result.get("decision_memo", "")
if memo:
st.markdown(memo)
else:
st.info("No decision memo available.")
with tab3:
st.subheader("Policy Checks")
checks = result.get("policy_checks", [])
st.subheader("Raw Output")
with st.expander("Show raw agent response JSON"):
st.json(st.session_state.raw_result or {})
for check in checks:
status_icon = {"PASS": "", "FAIL": "", "WARNING": "⚠️"}
st.markdown(
f"{status_icon.get(check['status'], '')} **{check['check']}**: {check['details']}"
)
st.divider()
exceptions = result.get("exceptions", [])
if exceptions:
st.subheader("⚠️ Policy Exceptions")
for exc in exceptions:
st.warning(exc)
st.divider()
st.subheader("📎 Required Documents")
docs = result.get("required_documents", [])
for doc in docs:
st.write(f"- {doc}")
with tab4:
st.subheader("Decision Memo")
st.markdown(result.get("decision_memo", "No memo available"))
with tab5:
st.subheader("Audit Trail")
audit = result.get("audit_trail", {})
st.json(audit)
# Case creation
st.divider()
st.header("📁 Case Management")
col1, col2 = st.columns([3, 1])
with col1:
if st.session_state.case_id:
st.success(f"✅ Case created: **{st.session_state.case_id}**")
else:
st.info(
"Create a case to store this assessment in the case management system"
)
with col2:
if not st.session_state.case_id:
if st.button("📁 Create Case", type="primary", use_container_width=True):
try:
# Create case via direct API
case_data = {
"applicant_name": result["normalized_application"][
"applicant_name"
],
"loan_amount": result["normalized_application"]["loan_amount"],
"risk_band": result["risk_band"],
"confidence": result["confidence"],
"recommended_action": result["recommended_action"],
"required_documents": result.get("required_documents", []),
"policy_exceptions": result.get("exceptions", []),
"notes": result.get("decision_memo", "")[:500],
}
response = httpx.post(
f"{CASE_SERVICE_URL}/cases", json=case_data, timeout=10.0
)
if response.status_code == 200:
case_result = response.json()
st.session_state.case_id = case_result["case_id"]
st.rerun()
else:
st.error(f"Failed to create case: {response.text}")
except Exception as e:
st.error(f"Error creating case: {str(e)}")
else:
if st.button("🔄 Reset", use_container_width=True):
st.session_state.case_id = None
st.session_state.assessment_result = None
st.rerun()
with st.expander("Show parsed assessment JSON"):
st.json(result)
else:
st.info(
"👈 Select a scenario or paste a loan application JSON in the sidebar, then click 'Assess Risk'"
"👈 Select a scenario or paste a loan application JSON in the sidebar, then click **Assess Risk**."
)
st.subheader("Sample Application Format")