From 43799ded85e4ae5d583ad3d532ca7464a7c51f04 Mon Sep 17 00:00:00 2001 From: Ahmed Burney Date: Fri, 16 Jan 2026 22:55:35 +0500 Subject: [PATCH] simplifying ui --- .../src/credit_risk_demo/ui_streamlit.py | 245 +++++++----------- 1 file changed, 91 insertions(+), 154 deletions(-) diff --git a/demos/use_cases/credit_risk_case_copilot/src/credit_risk_demo/ui_streamlit.py b/demos/use_cases/credit_risk_case_copilot/src/credit_risk_demo/ui_streamlit.py index 11c1c7fe..cfa06727 100644 --- a/demos/use_cases/credit_risk_case_copilot/src/credit_risk_demo/ui_streamlit.py +++ b/demos/use_cases/credit_risk_case_copilot/src/credit_risk_demo/ui_streamlit.py @@ -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 you’re 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")