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 json
import os import os
from datetime import datetime
import httpx import httpx
import streamlit as st import streamlit as st
# Configuration # Configuration
PLANO_ENDPOINT = os.getenv("PLANO_ENDPOINT", "http://localhost:8001/v1") PLANO_ENDPOINT = os.getenv("PLANO_ENDPOINT", "http://localhost:8001/v1")
CASE_SERVICE_URL = "http://localhost:10540"
st.set_page_config( st.set_page_config(
page_title="Credit Risk Case Copilot", page_title="Credit Risk Case Copilot",
@ -18,7 +16,7 @@ st.set_page_config(
# Load scenarios # Load scenarios
def load_scenario(scenario_file): def load_scenario(scenario_file: str):
"""Load scenario JSON from file.""" """Load scenario JSON from file."""
try: try:
with open(scenario_file, "r") as f: with open(scenario_file, "r") as f:
@ -30,13 +28,15 @@ def load_scenario(scenario_file):
# Initialize session state # Initialize session state
if "assessment_result" not in st.session_state: if "assessment_result" not in st.session_state:
st.session_state.assessment_result = None st.session_state.assessment_result = None
if "case_id" not in st.session_state: if "raw_result" not in st.session_state:
st.session_state.case_id = None st.session_state.raw_result = None
if "application_json" not in st.session_state:
st.session_state.application_json = "{}"
# Header # Header
st.title("🏦 Credit Risk Case Copilot") 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() st.divider()
# Sidebar # Sidebar
@ -67,38 +67,45 @@ with st.sidebar:
# JSON input area # JSON input area
application_json = st.text_area( application_json = st.text_area(
"Loan Application JSON", "Loan Application JSON",
value=st.session_state.get("application_json", "{}"), value=st.session_state.application_json,
height=400, height=380,
help="Paste or edit loan application JSON", help="Paste or edit loan application JSON",
) )
# Assess button col_a, col_b = st.columns(2)
if st.button("🔍 Assess Risk", type="primary", use_container_width=True):
try:
# Parse JSON
application_data = json.loads(application_json)
# Call Plano orchestrator with col_a:
with st.spinner("Running risk assessment..."): if st.button("🔍 Assess Risk", type="primary", use_container_width=True):
response = httpx.post( try:
f"{PLANO_ENDPOINT}/chat/completions", application_data = json.loads(application_json)
json={
"model": "gpt-4o", with st.spinner("Running risk assessment..."):
"messages": [ response = httpx.post(
{ f"{PLANO_ENDPOINT}/chat/completions",
"role": "user", json={
"content": f"Assess credit risk for this loan application:\n\n{json.dumps(application_data, indent=2)}", # Use risk_reasoning if youre standardizing on aliases.
} # If you want plain OpenAI model routing, set "gpt-4o".
], "model": "risk_reasoning",
}, "messages": [
timeout=60.0, {
) "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: if response.status_code == 200:
result = response.json() raw = response.json()
content = result["choices"][0]["message"]["content"] 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: if "```json" in content:
json_start = content.index("```json") + 7 json_start = content.index("```json") + 7
json_end = content.index("```", json_start) json_end = content.index("```", json_start)
@ -107,160 +114,90 @@ with st.sidebar:
st.session_state.assessment_result = assessment st.session_state.assessment_result = assessment
st.success("✅ Risk assessment complete!") st.success("✅ Risk assessment complete!")
else: 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: 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}") st.error(f"Error: {response.status_code} - {response.text}")
except json.JSONDecodeError: except json.JSONDecodeError:
st.error("Invalid JSON format") st.error("Invalid JSON format")
except Exception as e: except Exception as e:
st.error(f"Error: {str(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 # Main content area
if st.session_state.assessment_result: if st.session_state.assessment_result:
result = st.session_state.assessment_result result = st.session_state.assessment_result
# Risk summary st.header("Decision")
st.header("Risk Assessment Summary")
col1, col2, col3, col4 = st.columns(4) col1, col2, col3 = st.columns(3)
with col1: with col1:
risk_color = {"LOW": "🟢", "MEDIUM": "🟡", "HIGH": "🔴"} risk_color = {"LOW": "🟢", "MEDIUM": "🟡", "HIGH": "🔴"}
st.metric( risk_band = result.get("risk_band", "UNKNOWN")
"Risk Band", st.metric("Risk Band", f"{risk_color.get(risk_band, '')} {risk_band}")
f"{risk_color.get(result['risk_band'], '')} {result['risk_band']}",
)
with col2: 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: with col3:
st.metric("Recommended Action", result["recommended_action"]) st.metric("Recommended Action", result.get("recommended_action", "REVIEW"))
with col4:
st.metric("Documents Required", len(result.get("required_documents", [])))
st.divider() st.divider()
# Tabbed interface tab1, tab2, tab3 = st.tabs(["🧾 Summary", "📝 Decision Memo", "🧪 Raw Output"])
tab1, tab2, tab3, tab4, tab5 = st.tabs(
[
"📊 Risk Summary",
"🎯 Risk Drivers",
"📋 Policy & Compliance",
"📝 Decision Memo",
"🔍 Audit Trail",
]
)
with tab1: 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.subheader("Normalized Application")
st.json(result.get("normalized_application", {})) st.json(result.get("normalized_application", {}))
st.subheader("Assessment Overview")
st.write(result.get("human_response", "").split("```")[0])
with tab2: with tab2:
st.subheader("Risk Drivers") st.subheader("Decision Memo")
drivers = result.get("drivers", []) memo = result.get("decision_memo", "")
if memo:
for driver in drivers: st.markdown(memo)
impact_color = {"CRITICAL": "🔴", "HIGH": "🟠", "MEDIUM": "🟡", "LOW": "🟢"} else:
st.markdown( st.info("No decision memo available.")
f"**{impact_color.get(driver['impact'], '')} {driver['factor']}** ({driver['impact']})"
)
st.write(driver["evidence"])
st.divider()
with tab3: with tab3:
st.subheader("Policy Checks") st.subheader("Raw Output")
checks = result.get("policy_checks", []) with st.expander("Show raw agent response JSON"):
st.json(st.session_state.raw_result or {})
for check in checks: with st.expander("Show parsed assessment JSON"):
status_icon = {"PASS": "", "FAIL": "", "WARNING": "⚠️"} st.json(result)
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()
else: else:
st.info( 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") st.subheader("Sample Application Format")