import streamlit as st import requests import os import time import re import yaml # Set minimal, professional industry-appropriate page configuration st.set_page_config( page_title="LiteLLM Gateway Console", page_icon="🛡️", layout="wide", initial_sidebar_state="collapsed" ) # Professional Minimal CSS (Light Theme / Corporate Dark Accent) st.markdown(""" """, unsafe_allow_html=True) # Port configuration — reads PROXY_URL directly (for cloud environments) # or falls back to http://127.0.0.1:PORT (local dev) PROXY_URL = os.environ.get("PROXY_URL", f"http://127.0.0.1:{os.environ.get('PORT', '8000')}") # Dynamically parse config.yaml models CONFIG_FILE = os.path.join(os.path.dirname(__file__), "config.yaml") def load_yaml_config(): if os.path.exists(CONFIG_FILE): try: with open(CONFIG_FILE, "r", encoding="utf-8") as f: return yaml.safe_load(f) except Exception: pass return {} def load_pii_config(): try: r = requests.get(f"{PROXY_URL}/ui/pii-config", timeout=2.0) if r.status_code == 200: return r.json() except Exception: pass return {"pii_enabled": False, "pii_action": "MASK"} # Dynamically parse config.yaml models and default specs config_data = load_yaml_config() model_list = config_data.get("model_list", []) configured_models = [] model_defaults = {} for item in model_list: name = item.get("model_name") if name and name not in configured_models: configured_models.append(name) params = item.get("litellm_params", {}) sub_model = params.get("model") if sub_model and sub_model not in configured_models: configured_models.append(sub_model) tpm_val = params.get("tpm", 50000) cost_per_mil = params.get("cost_per_million", 0.05) cost_per_k = cost_per_mil / 1000.0 if name and name not in model_defaults: model_defaults[name] = {"tpm": tpm_val, "cost": cost_per_k} if sub_model and sub_model not in model_defaults: model_defaults[sub_model] = {"tpm": tpm_val, "cost": cost_per_k} if not configured_models: configured_models = ["primary-cluster", "backup-cluster", "groq/llama-3.1-8b-instant", "cerebras/llama3.1-8b"] # Ensure session states exist if "agent_name" not in st.session_state: st.session_state.agent_name = "Credit Assessment Manager" if "model_priorities" not in st.session_state: st.session_state.model_priorities = [configured_models[0]] if configured_models else ["primary-cluster"] if "tpm_limits" not in st.session_state: st.session_state.tpm_limits = {} if "cost_limits" not in st.session_state: st.session_state.cost_limits = {} if "history" not in st.session_state: st.session_state.history = [] if "last_result" not in st.session_state: st.session_state.last_result = None if "cost_limit" not in st.session_state: st.session_state.cost_limit = 5.00 # --------------------------------------------------------------------- # SIDEBAR - AGENT DELEGATION # --------------------------------------------------------------------- with st.sidebar: st.markdown("

Agent Delegation

", unsafe_allow_html=True) st.write("Define and delegate context-specific configurations to an autonomous agent.") agent_options = [ "Credit Assessment Manager", "Customer Support Specialist", "Legal Compliance Auditor", "Custom Agent..." ] selected_agent = st.selectbox( "Active Agent Designation", options=agent_options, index=agent_options.index(st.session_state.agent_name) if st.session_state.agent_name in agent_options else 0 ) if selected_agent == "Custom Agent...": custom_name = st.text_input("Define Custom Agent Name", value="Risk Analyst Agent") st.session_state.agent_name = custom_name else: st.session_state.agent_name = selected_agent st.markdown("---") st.markdown("

Agent Context

", unsafe_allow_html=True) if st.session_state.agent_name == "Credit Assessment Manager": st.caption("Delegated to evaluate financial eligibility, credit ratings, credit card limits, and risk profiles.") elif st.session_state.agent_name == "Customer Support Specialist": st.caption("Delegated to resolve general client queries.") elif st.session_state.agent_name == "Legal Compliance Auditor": st.caption("Delegated to audit legal agreements, contract values, and compliance status.") else: st.caption("Custom agent rules dynamically configured across the current execution path.") # --------------------------------------------------------------------- # MAIN INTERFACE TABS (Page 1 vs Page 2) # --------------------------------------------------------------------- st.markdown("
LiteLLM Gateway Console & Routing Proxy
", unsafe_allow_html=True) st.markdown(f"
Minimalist enterprise routing controls configured for {st.session_state.agent_name}
", unsafe_allow_html=True) tab_backend, tab_testing = st.tabs([ "Page 1: Backend Gateway Controls", "Page 2: User Testing Interface" ]) # --------------------------------------------------------------------- # PAGE 1: BACKEND CONTROLS # --------------------------------------------------------------------- with tab_backend: col_llm, col_guardrails = st.columns([1, 1], gap="large") with col_llm: st.markdown("
LLM Prioritization & Limits
", unsafe_allow_html=True) st.write("Prioritize execution paths based on TPR/TPM loads. LiteLLM handles fallback routing dynamically.") # 1. LLM priorities order = st.multiselect( "Priority Routing Hierarchy", options=configured_models, default=st.session_state.model_priorities if all(m in configured_models for m in st.session_state.model_priorities) else [configured_models[0]], help="LiteLLM Gateway will route requests down this chain on congestion or failure." ) if order: st.session_state.model_priorities = order # 2. Individual Model Limits and Costs st.markdown("
", unsafe_allow_html=True) st.markdown("Individual Model Settings", unsafe_allow_html=True) for m in st.session_state.model_priorities: defaults = model_defaults.get(m, {"tpm": 50000, "cost": 0.05}) # Setup session state key defaults if f"tpm_{m}" not in st.session_state: st.session_state[f"tpm_{m}"] = defaults["tpm"] if f"cost_{m}" not in st.session_state: st.session_state[f"cost_{m}"] = defaults["cost"] st.markdown(f"
↳ {m}
", unsafe_allow_html=True) col_tpm, col_cost = st.columns(2) with col_tpm: st.session_state.tpm_limits[m] = st.number_input( f"TPM Limit", min_value=1000, max_value=1000000, key=f"tpm_{m}", step=5000 ) with col_cost: st.session_state.cost_limits[m] = st.number_input( f"Cost/1K Tokens ($)", min_value=0.00001, max_value=100.0, key=f"cost_{m}", format="%.6f", step=0.001 ) st.markdown("
", unsafe_allow_html=True) st.info("ℹ️ LiteLLM load balancer distributes prompt workloads via the dynamic `usage-based-routing` strategy mapped in `config.yaml`.") with col_guardrails: st.markdown("
PII Guardrail Controls (DeBERTa-v3)
", unsafe_allow_html=True) st.write("Apply real-time PII detection and remediation to prompt inputs and model responses.") pii_cfg = load_pii_config() pii_enabled_default = pii_cfg.get("pii_enabled", False) pii_action_default = pii_cfg.get("pii_action", "MASK") pii_enabled = st.toggle( "Enable PII Guardrail", value=pii_enabled_default, help="Scan user prompt inputs and generated model outputs for personally identifiable information (PII)." ) actions = ["BLOCK", "MASK", "REWRITE"] pii_action = st.selectbox( "Default PII Remediation Action", options=actions, index=actions.index(pii_action_default) if pii_action_default in actions else 1, help="Choose the default remediation strategy. Individual overrides can be set below." ) st.markdown("
", unsafe_allow_html=True) st.markdown("Entity-Level Remediation Policies", unsafe_allow_html=True) pii_policy_default = pii_cfg.get("pii_policy") or {} if not isinstance(pii_policy_default, dict): pii_policy_default = {} PII_ENTITIES = { "person": "Name", "phone number": "Phone Number", "social security number": "SSN / Aadhaar", "credit card number": "Credit Card", "api key": "API Key", "email address": "Email Address", "address": "Address", "bank account number": "Bank Account Number", "passport number": "Passport Number", "password": "Password" } pii_policy = {} with st.expander("Configure Specific PII Types", expanded=True): col_ent1, col_ent2 = st.columns(2) for idx, (entity_key, entity_label) in enumerate(PII_ENTITIES.items()): col = col_ent1 if idx % 2 == 0 else col_ent2 with col: default_action = pii_policy_default.get(entity_key, pii_action) options = ["BLOCK", "MASK", "REWRITE", "IGNORE"] selected_action = st.selectbox( entity_label, options=options, index=options.index(default_action) if default_action in options else options.index(pii_action), key=f"pii_action_{entity_key}" ) pii_policy[entity_key] = selected_action st.markdown("
", unsafe_allow_html=True) if st.button("Apply PII Policy", type="primary", use_container_width=True): payload = { "pii_enabled": pii_enabled, "pii_action": pii_action, "pii_policy": pii_policy } try: r = requests.post(f"{PROXY_URL}/ui/pii-config", json=payload, timeout=5.0) if r.status_code == 200: st.success("PII Guardrail configuration updated successfully.") time.sleep(0.5) st.rerun() else: st.error(f"Failed to save configuration: {r.text}") except Exception as e: st.error(f"Could not connect to proxy server: {e}") # --------------------------------------------------------------------- # PAGE 2: USER TESTING INTERFACE # --------------------------------------------------------------------- with tab_testing: st.markdown("
LLM Unified Gateway Endpoint
", unsafe_allow_html=True) st.markdown(f"
POST   {PROXY_URL}/v1/chat/completions
", unsafe_allow_html=True) st.write("") # User Input Query user_query = st.text_area( "Enter Query Prompt to Test", value="I am Samuel, my phone numbers are +1 213 555-0123 and +91 9876534567 , Aadhaar is 9988-7766-5544, SSN: 111-22-3333. my email is sam@gmail.com, check which all accounts are linked together? ", height=120 ) if st.button("Submit Request", type="primary"): # Trigger Gateway Call payload = { "model": st.session_state.model_priorities[0] if st.session_state.model_priorities else "oss-chat-fast", "messages": [{"role": "user", "content": user_query}], "temperature": 0.3, "max_tokens": 150 } start_time = time.time() backend_response = "" actual_model = "Mock-Fallback-Node" latency = 0.0 try: r = requests.post(f"{PROXY_URL}/v1/chat/completions", json=payload, timeout=15) latency = time.time() - start_time if r.status_code == 200: data = r.json() backend_response = data["choices"][0]["message"]["content"] actual_model = data.get("model", payload["model"]) guardrailed_query = data.get("guardrailed_query", user_query) elif r.status_code == 400: try: error_detail = r.json().get("detail", r.text) except Exception: error_detail = r.text backend_response = f"⚠️ Request Blocked: {error_detail}" actual_model = "Blocked (PII Policy)" guardrailed_query = None else: backend_response = f"⚠️ Gateway Error ({r.status_code}): {r.text}" guardrailed_query = None except Exception as e: # Fallback mock response for testing disconnected modes latency = 0.045 backend_response = f"This is a simulated response from the gateway node running in local sandbox mode." actual_model = f"{st.session_state.model_priorities[0]} (Local Sandbox Simulation)" guardrailed_query = user_query # Save to stateful variables st.session_state.last_result = { "query": user_query, "response": backend_response, "model": actual_model, "latency": latency, "guardrailed_query": guardrailed_query } st.session_state.history.append(st.session_state.last_result) # Render last result if present if st.session_state.last_result: res = st.session_state.last_result # Display stacked comparative layout with wide response space col_prompts, col_response = st.columns([1.2, 1.8], gap="large") with col_prompts: st.markdown("ORIGINAL QUERY", unsafe_allow_html=True) st.markdown(f"
{res['query']}
", unsafe_allow_html=True) # Display guardrailed query below original query st.markdown("
", unsafe_allow_html=True) st.markdown("GUARDRAILED QUERY (SENT TO LLM)", unsafe_allow_html=True) g_query = res.get("guardrailed_query") if g_query is None: st.markdown("
[BLOCKED - NOT SENT TO LLM]
", unsafe_allow_html=True) elif g_query == res['query']: st.markdown(f"
{g_query}
", unsafe_allow_html=True) else: st.markdown(f"
{g_query}
", unsafe_allow_html=True) with col_response: st.markdown("GATEWAY RESPONSE", unsafe_allow_html=True) st.markdown(f"
{res['response']}
", unsafe_allow_html=True) # Display professional HUD Metrics st.markdown("---") col_hud1, col_hud2, col_hud3, col_hud4 = st.columns(4) with col_hud1: st.markdown("
Active Agent
", unsafe_allow_html=True) st.markdown(f"
{st.session_state.agent_name}
", unsafe_allow_html=True) with col_hud2: st.markdown("
Latency
", unsafe_allow_html=True) st.markdown(f"
{res['latency']:.3f}s
", unsafe_allow_html=True) with col_hud3: st.markdown("
Target Routing Cluster
", unsafe_allow_html=True) st.markdown(f"
{res['model']}
", unsafe_allow_html=True) with col_hud4: model_tpm = st.session_state.tpm_limits.get(res['model'], 50000) model_cost = st.session_state.cost_limits.get(res['model'], 0.05) st.markdown("
Limits & Capacity
", unsafe_allow_html=True) st.markdown(f"
{model_tpm} TPM / ${model_cost:.5f}
", unsafe_allow_html=True) # Past queries of the session if st.session_state.history: st.markdown("
", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) st.markdown("
Session Queries History
", unsafe_allow_html=True) for idx, item in enumerate(reversed(st.session_state.history)): turn_idx = len(st.session_state.history) - idx with st.expander(f"Turn #{turn_idx} — Model: {item['model']} — Latency: {item['latency']:.3f}s"): col_hist_left, col_hist_right = st.columns([1, 1]) with col_hist_left: st.markdown("Original Query", unsafe_allow_html=True) st.info(item["query"]) g_query = item.get("guardrailed_query") if g_query is None: st.markdown("Guardrail Action", unsafe_allow_html=True) st.error("Blocked — Not sent to LLM") elif g_query != item["query"]: st.markdown("Guardrailed Query (Sent to LLM)", unsafe_allow_html=True) st.warning(g_query) with col_hist_right: st.markdown("Gateway Response", unsafe_allow_html=True) st.success(item["response"])