Spaces:
Build error
Build error
| import streamlit as st | |
| import pandas as pd | |
| import numpy as np | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| from streamlit_option_menu import option_menu | |
| import time | |
| from faker import Faker | |
| from datetime import datetime, timedelta | |
| import random | |
| import json | |
| from typing import Dict, List, Any | |
| import os | |
| # Page configuration | |
| st.set_page_config( | |
| page_title="SAP S/4HANA Agentic AI Procurement Analytics", | |
| page_icon="π€", | |
| layout="wide", | |
| initial_sidebar_state="expanded" | |
| ) | |
| # Custom CSS | |
| st.markdown(""" | |
| <style> | |
| /* Main theme colors */ | |
| :root { | |
| --primary-color: #0066cc; | |
| --secondary-color: #f0f8ff; | |
| --accent-color: #ff6b35; | |
| --success-color: #28a745; | |
| --warning-color: #ffc107; | |
| --danger-color: #dc3545; | |
| } | |
| /* Hide Streamlit branding */ | |
| #MainMenu {visibility: hidden;} | |
| footer {visibility: hidden;} | |
| header {visibility: hidden;} | |
| /* Custom header styling */ | |
| .main-header { | |
| background: linear-gradient(90deg, #0066cc, #004c99); | |
| padding: 1rem; | |
| border-radius: 10px; | |
| margin-bottom: 2rem; | |
| color: white; | |
| text-align: center; | |
| } | |
| /* Metric cards styling */ | |
| .metric-card { | |
| background: white; | |
| padding: 1.5rem; | |
| border-radius: 10px; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
| border-left: 4px solid var(--primary-color); | |
| margin-bottom: 1rem; | |
| } | |
| /* AI insights styling */ | |
| .ai-insight { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| padding: 1rem; | |
| border-radius: 10px; | |
| margin: 1rem 0; | |
| } | |
| /* Alert styling */ | |
| .alert { | |
| padding: 1rem; | |
| border-radius: 8px; | |
| margin: 1rem 0; | |
| border-left: 4px solid; | |
| } | |
| .alert-success { | |
| background-color: #d4edda; | |
| border-color: var(--success-color); | |
| color: #155724; | |
| } | |
| .alert-warning { | |
| background-color: #fff3cd; | |
| border-color: var(--warning-color); | |
| color: #856404; | |
| } | |
| .alert-info { | |
| background-color: #d1ecf1; | |
| border-color: #17a2b8; | |
| color: #0c5460; | |
| } | |
| /* Button styling */ | |
| .stButton > button { | |
| background: linear-gradient(90deg, #0066cc, #004c99); | |
| color: white; | |
| border: none; | |
| border-radius: 8px; | |
| padding: 0.5rem 1rem; | |
| font-weight: 600; | |
| transition: all 0.3s ease; | |
| } | |
| .stButton > button:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 4px 8px rgba(0,0,0,0.2); | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # Function to safely get OpenAI API key | |
| def get_openai_api_key(): | |
| """Safely retrieve OpenAI API key from various sources""" | |
| api_key = None | |
| # Method 1: Try from Streamlit secrets | |
| try: | |
| if hasattr(st, 'secrets') and 'OPENAI_API_KEY' in st.secrets: | |
| api_key = st.secrets["OPENAI_API_KEY"] | |
| except Exception: | |
| pass | |
| # Method 2: Try from environment variables | |
| if not api_key: | |
| api_key = os.getenv('OPENAI_API_KEY') | |
| # Method 3: Try from Hugging Face Spaces environment | |
| if not api_key: | |
| api_key = os.getenv('OPENAI_API_TOKEN') # Sometimes HF uses this | |
| return api_key | |
| # Data generation function | |
| def generate_synthetic_procurement_data(): | |
| """Generate synthetic SAP S/4HANA procurement data""" | |
| fake = Faker() | |
| np.random.seed(42) # For reproducible data | |
| random.seed(42) | |
| # Vendors data | |
| vendors = [ | |
| "Siemens AG", "BASF SE", "BMW Group", "Mercedes-Benz", "Bosch GmbH", | |
| "ThyssenKrupp", "Bayer AG", "Continental AG", "Henkel AG", "SAP SE" | |
| ] | |
| # Material categories | |
| material_categories = [ | |
| "Raw Materials", "Components", "Packaging", "Services", | |
| "IT Equipment", "Office Supplies", "Machinery", "Chemicals" | |
| ] | |
| # Generate purchase orders | |
| purchase_orders = [] | |
| for i in range(500): | |
| order_date = fake.date_between(start_date='-2y', end_date='today') | |
| delivery_date = order_date + timedelta(days=random.randint(1, 30)) | |
| po = { | |
| 'po_number': f"PO{str(i+1).zfill(6)}", | |
| 'vendor': random.choice(vendors), | |
| 'material_category': random.choice(material_categories), | |
| 'order_date': order_date, | |
| 'delivery_date': delivery_date, | |
| 'order_value': round(random.uniform(1000, 100000), 2), | |
| 'quantity': random.randint(1, 1000), | |
| 'unit_price': round(random.uniform(10, 500), 2), | |
| 'status': random.choice(['Open', 'Delivered', 'Invoiced', 'Paid']), | |
| 'plant': random.choice(['Plant_001', 'Plant_002', 'Plant_003']), | |
| 'buyer': fake.name(), | |
| 'currency': 'EUR', | |
| 'payment_terms': random.choice(['30 Days', '60 Days', '90 Days']), | |
| 'on_time_delivery': random.choice([True, False]), | |
| 'quality_score': round(random.uniform(7, 10), 1) | |
| } | |
| purchase_orders.append(po) | |
| # Generate spend analytics data | |
| spend_data = [] | |
| for vendor in vendors: | |
| for category in material_categories: | |
| spend = { | |
| 'vendor': vendor, | |
| 'category': category, | |
| 'total_spend': round(random.uniform(10000, 500000), 2), | |
| 'contract_compliance': round(random.uniform(80, 100), 1), | |
| 'risk_score': round(random.uniform(1, 10), 1), | |
| 'savings_potential': round(random.uniform(5, 25), 1) | |
| } | |
| spend_data.append(spend) | |
| return pd.DataFrame(purchase_orders), pd.DataFrame(spend_data) | |
| # AI Agent Classes | |
| class LLMPoweredProcurementAgent: | |
| """AI Agent powered by OpenAI GPT for intelligent procurement analysis""" | |
| def __init__(self, po_data: pd.DataFrame, spend_data: pd.DataFrame): | |
| self.po_data = po_data | |
| self.spend_data = spend_data | |
| # Safely get OpenAI API key | |
| self.api_key = get_openai_api_key() | |
| self.llm_available = bool(self.api_key) | |
| if self.llm_available: | |
| try: | |
| import openai | |
| self.client = openai.OpenAI(api_key=self.api_key) | |
| except ImportError: | |
| self.llm_available = False | |
| self.client = None | |
| else: | |
| self.client = None | |
| def generate_executive_summary(self) -> str: | |
| """Generate an executive summary using GPT or fallback""" | |
| if not self.llm_available: | |
| # Enhanced rule-based summary if no API key | |
| total_spend = self.po_data['order_value'].sum() | |
| total_orders = len(self.po_data) | |
| on_time_rate = self.po_data['on_time_delivery'].mean() * 100 | |
| quality_avg = self.po_data['quality_score'].mean() | |
| top_category = self.po_data.groupby('material_category')['order_value'].sum().idxmax() | |
| top_vendor = self.po_data.groupby('vendor')['order_value'].sum().idxmax() | |
| return f"""**π― Executive Summary - Procurement Performance Dashboard** | |
| π **Current Portfolio Overview** | |
| β’ Total procurement spend: β¬{total_spend:,.0f} across {total_orders:,} purchase orders | |
| β’ Active vendor network: {len(self.po_data['vendor'].unique())} strategic suppliers | |
| β’ Average order value: β¬{self.po_data['order_value'].mean():,.0f} | |
| π **Performance Highlights** | |
| β’ On-time delivery performance: {on_time_rate:.1f}% (Industry benchmark: 85%) | |
| β’ Average supplier quality score: {quality_avg:.1f}/10 | |
| β’ Leading spend category: {top_category} | |
| β’ Top strategic partner: {top_vendor} | |
| β‘ **Strategic Opportunities** | |
| β’ Vendor consolidation potential identified in {len(self.po_data['vendor'].unique())} supplier base | |
| β’ Contract optimization opportunities with top-tier vendors | |
| β’ Digital procurement automation possibilities for routine purchases | |
| π‘ **AI-Powered Recommendations** | |
| β’ Implement strategic sourcing for {top_category} category | |
| β’ Develop performance-based contracts with high-performing suppliers | |
| β’ Establish automated approval workflows for orders under β¬10,000 | |
| *π§ Note: Connect OpenAI API for advanced AI insights and natural language analysis*""" | |
| # Prepare data summary for LLM | |
| data_summary = { | |
| "total_spend": float(self.po_data['order_value'].sum()), | |
| "total_orders": len(self.po_data), | |
| "unique_vendors": len(self.po_data['vendor'].unique()), | |
| "avg_order_value": float(self.po_data['order_value'].mean()), | |
| "on_time_delivery_rate": float(self.po_data['on_time_delivery'].mean()), | |
| "top_vendors": self.po_data.groupby('vendor')['order_value'].sum().nlargest(3).to_dict(), | |
| "top_categories": self.po_data.groupby('material_category')['order_value'].sum().nlargest(3).to_dict(), | |
| "quality_score_avg": float(self.po_data['quality_score'].mean()) | |
| } | |
| prompt = f""" | |
| As a senior procurement analyst with expertise in SAP S/4HANA systems, provide an executive summary of procurement performance: | |
| Data: {json.dumps(data_summary, indent=2)} | |
| Provide: | |
| 1. Executive overview (2-3 sentences) | |
| 2. Key performance highlights with specific metrics | |
| 3. Critical areas needing attention | |
| 4. Strategic recommendations (3-4 actionable items) | |
| Keep it professional, metrics-focused, and actionable for C-level executives. | |
| """ | |
| try: | |
| response = self.client.chat.completions.create( | |
| model="gpt-4", | |
| messages=[ | |
| {"role": "system", "content": "You are a senior procurement analyst with 15+ years of SAP S/4HANA experience."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| max_tokens=600, | |
| temperature=0.7 | |
| ) | |
| return response.choices[0].message.content | |
| except Exception as e: | |
| return f"π€ AI Analysis temporarily unavailable. Using rule-based insights instead.\n\n{self.generate_executive_summary()}" | |
| def chat_with_data(self, user_question: str) -> str: | |
| """Natural language interface to query procurement data""" | |
| if not self.llm_available: | |
| # Enhanced rule-based responses | |
| question_lower = user_question.lower() | |
| if any(word in question_lower for word in ["spend", "cost", "money", "budget"]): | |
| total_spend = self.po_data['order_value'].sum() | |
| top_category = self.po_data.groupby('material_category')['order_value'].sum().idxmax() | |
| monthly_avg = total_spend / 24 # Assuming 2 years of data | |
| return f"""π° **Spend Analysis:** | |
| β’ **Total procurement spend**: β¬{total_spend:,.0f} | |
| β’ **Monthly average**: β¬{monthly_avg:,.0f} | |
| β’ **Largest spend category**: {top_category} | |
| β’ **Average order size**: β¬{self.po_data['order_value'].mean():,.0f} | |
| The spending is distributed across {len(self.po_data['material_category'].unique())} categories with {top_category} representing the highest investment area. | |
| *π‘ Connect OpenAI API for detailed spend optimization strategies!*""" | |
| elif any(word in question_lower for word in ["vendor", "supplier", "partner"]): | |
| top_vendor = self.po_data.groupby('vendor')['order_value'].sum().idxmax() | |
| vendor_count = len(self.po_data['vendor'].unique()) | |
| top_vendor_performance = self.po_data[self.po_data['vendor'] == top_vendor]['on_time_delivery'].mean() * 100 | |
| return f"""π€ **Vendor Analysis:** | |
| β’ **Total active vendors**: {vendor_count} | |
| β’ **Top strategic partner**: {top_vendor} | |
| β’ **{top_vendor} performance**: {top_vendor_performance:.1f}% on-time delivery | |
| β’ **Vendor diversity**: Well-distributed across multiple suppliers | |
| Your vendor portfolio shows good diversification with {top_vendor} as the leading partner. | |
| *π‘ Connect OpenAI API for detailed vendor relationship strategies!*""" | |
| elif any(word in question_lower for word in ["risk", "compliance", "quality"]): | |
| avg_quality = self.po_data['quality_score'].mean() | |
| on_time_rate = self.po_data['on_time_delivery'].mean() * 100 | |
| return f"""β οΈ **Risk & Quality Analysis:** | |
| β’ **Average quality score**: {avg_quality:.1f}/10 | |
| β’ **On-time delivery rate**: {on_time_rate:.1f}% | |
| β’ **Performance status**: {'Excellent' if avg_quality > 8.5 else 'Good' if avg_quality > 7.5 else 'Needs Improvement'} | |
| Overall risk profile appears {'low' if on_time_rate > 85 else 'moderate'} based on delivery performance metrics. | |
| *π‘ Connect OpenAI API for comprehensive risk assessment!*""" | |
| elif any(word in question_lower for word in ["trend", "pattern", "analysis"]): | |
| return f"""π **Trend Analysis:** | |
| β’ **Data period**: {self.po_data['order_date'].min()} to {self.po_data['order_date'].max()} | |
| β’ **Total orders processed**: {len(self.po_data):,} | |
| β’ **Peak category**: {self.po_data.groupby('material_category')['order_value'].sum().idxmax()} | |
| β’ **Seasonal patterns**: Data shows consistent procurement activity | |
| Historical data indicates stable procurement operations with opportunities for optimization. | |
| *π‘ Connect OpenAI API for advanced trend forecasting!*""" | |
| else: | |
| return f"""π€ **Procurement Assistant Ready!** | |
| I can help you analyze: | |
| β’ π° **Spending patterns** and budget optimization | |
| β’ π€ **Vendor performance** and relationship management | |
| β’ β οΈ **Risk assessment** and quality metrics | |
| β’ π **Trends and forecasting** for strategic planning | |
| **Current data scope**: {len(self.po_data):,} orders across {len(self.po_data['vendor'].unique())} vendors | |
| Try asking: "What are my biggest spending areas?" or "How are my vendors performing?" | |
| *π‘ Connect OpenAI API for natural language conversations and advanced insights!*""" | |
| # LLM-powered response | |
| data_context = { | |
| "procurement_summary": { | |
| "total_spend": float(self.po_data['order_value'].sum()), | |
| "order_count": len(self.po_data), | |
| "vendor_count": len(self.po_data['vendor'].unique()), | |
| "date_range": f"{self.po_data['order_date'].min()} to {self.po_data['order_date'].max()}", | |
| "categories": self.po_data['material_category'].unique().tolist(), | |
| "vendors": self.po_data['vendor'].unique().tolist() | |
| }, | |
| "performance_metrics": { | |
| "avg_quality_score": float(self.po_data['quality_score'].mean()), | |
| "on_time_delivery_rate": float(self.po_data['on_time_delivery'].mean()), | |
| "avg_order_value": float(self.po_data['order_value'].mean()) | |
| } | |
| } | |
| prompt = f""" | |
| User Question: {user_question} | |
| Procurement Data Context: | |
| {json.dumps(data_context, indent=2)} | |
| Answer the user's question based on the procurement data. Be conversational yet professional. | |
| Include specific metrics when relevant and relate findings to business impact. | |
| If you need additional data not available in the context, suggest what analysis would be helpful. | |
| """ | |
| try: | |
| response = self.client.chat.completions.create( | |
| model="gpt-4", | |
| messages=[ | |
| {"role": "system", "content": "You are an expert procurement analyst assistant. Provide helpful, professional responses about procurement data and strategy."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| max_tokens=500, | |
| temperature=0.7 | |
| ) | |
| return response.choices[0].message.content | |
| except Exception as e: | |
| return f"I'm having trouble accessing advanced AI right now. Here's what I can tell you based on the data:\n\n{self.chat_with_data(user_question)}" | |
| def analyze_spend_patterns(self) -> Dict[str, Any]: | |
| """Analyze spending patterns and generate insights""" | |
| total_spend = self.po_data['order_value'].sum() | |
| avg_order_value = self.po_data['order_value'].mean() | |
| # Top spending categories | |
| category_spend = self.po_data.groupby('material_category')['order_value'].sum().sort_values(ascending=False) | |
| # Vendor performance analysis | |
| vendor_performance = self.po_data.groupby('vendor').agg({ | |
| 'order_value': 'sum', | |
| 'on_time_delivery': 'mean', | |
| 'quality_score': 'mean' | |
| }).round(2) | |
| return { | |
| 'total_spend': total_spend, | |
| 'avg_order_value': avg_order_value, | |
| 'top_categories': category_spend.to_dict(), | |
| 'vendor_performance': vendor_performance.to_dict('index') | |
| } | |
| # Initialize session state and data | |
| if 'data_loaded' not in st.session_state: | |
| with st.spinner('π Generating synthetic SAP S/4HANA procurement data...'): | |
| st.session_state.po_df, st.session_state.spend_df = generate_synthetic_procurement_data() | |
| st.session_state.data_loaded = True | |
| # Initialize AI agents | |
| def initialize_agents(): | |
| analytics_agent = LLMPoweredProcurementAgent(st.session_state.po_df, st.session_state.spend_df) | |
| return analytics_agent | |
| analytics_agent = initialize_agents() | |
| # API Key status check | |
| api_key = get_openai_api_key() | |
| api_key_status = "π’ Connected" if api_key else "π΄ Not Connected" | |
| # Main header | |
| st.markdown(f""" | |
| <div class="main-header"> | |
| <h1>π€ SAP S/4HANA Agentic AI Procurement Analytics</h1> | |
| <p>Autonomous Intelligence for Procurement Excellence</p> | |
| <small>OpenAI Status: {api_key_status} | Data: {len(st.session_state.po_df):,} Purchase Orders</small> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Sidebar navigation | |
| with st.sidebar: | |
| st.markdown("### π€ AI-Powered Analytics") | |
| st.markdown(f"**OpenAI Status:** {api_key_status}") | |
| if not api_key: | |
| st.markdown(""" | |
| <div class="alert alert-info"> | |
| <small><strong>π‘ Enhanced AI Features</strong><br> | |
| Add OpenAI API key as OPENAI_API_KEY in your Hugging Face Space settings for advanced AI conversations and insights!</small> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| st.markdown("---") | |
| selected = option_menu( | |
| "Navigation", | |
| ["π Dashboard", "π¬ AI Chat", "π Analytics", "π― Recommendations"], | |
| icons=['house', 'chat', 'graph-up', 'target'], | |
| menu_icon="cast", | |
| default_index=0, | |
| styles={ | |
| "container": {"padding": "0!important", "background-color": "#fafafa"}, | |
| "icon": {"color": "#0066cc", "font-size": "18px"}, | |
| "nav-link": {"font-size": "16px", "text-align": "left", "margin": "0px", "--hover-color": "#eee"}, | |
| "nav-link-selected": {"background-color": "#0066cc"}, | |
| } | |
| ) | |
| st.markdown("---") | |
| st.markdown("### π Quick Stats") | |
| st.metric("Total Orders", f"{len(st.session_state.po_df):,}") | |
| st.metric("Active Vendors", f"{len(st.session_state.po_df['vendor'].unique())}") | |
| st.metric("Categories", f"{len(st.session_state.po_df['material_category'].unique())}") | |
| if selected == "π Dashboard": | |
| # AI-generated insights at the top | |
| st.markdown("### π§ AI Executive Summary") | |
| with st.spinner('π€ AI analyzing procurement data...'): | |
| executive_summary = analytics_agent.generate_executive_summary() | |
| st.markdown(f""" | |
| <div class="ai-insight"> | |
| <h4>π Intelligent Analysis</h4> | |
| <div style="white-space: pre-line; line-height: 1.6;">{executive_summary}</div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Key metrics | |
| insights = analytics_agent.analyze_spend_patterns() | |
| col1, col2, col3, col4 = st.columns(4) | |
| with col1: | |
| st.markdown(""" | |
| <div class="metric-card"> | |
| <h3 style="color: var(--primary-color); margin: 0;">Total Spend</h3> | |
| <h2 style="margin: 0.5rem 0;">β¬{:,.0f}</h2> | |
| <p style="color: #28a745; margin: 0;">π Active Portfolio</p> | |
| </div> | |
| """.format(insights['total_spend']), unsafe_allow_html=True) | |
| with col2: | |
| st.markdown(""" | |
| <div class="metric-card"> | |
| <h3 style="color: var(--primary-color); margin: 0;">Avg Order Value</h3> | |
| <h2 style="margin: 0.5rem 0;">β¬{:,.0f}</h2> | |
| <p style="color: #17a2b8; margin: 0;">π Order Efficiency</p> | |
| </div> | |
| """.format(insights['avg_order_value']), unsafe_allow_html=True) | |
| with col3: | |
| active_vendors = len(st.session_state.po_df['vendor'].unique()) | |
| st.markdown(""" | |
| <div class="metric-card"> | |
| <h3 style="color: var(--primary-color); margin: 0;">Active Vendors</h3> | |
| <h2 style="margin: 0.5rem 0;">{}</h2> | |
| <p style="color: #6f42c1; margin: 0;">π€ Strategic Partners</p> | |
| </div> | |
| """.format(active_vendors), unsafe_allow_html=True) | |
| with col4: | |
| on_time_delivery = st.session_state.po_df['on_time_delivery'].mean() * 100 | |
| st.markdown(""" | |
| <div class="metric-card"> | |
| <h3 style="color: var(--primary-color); margin: 0;">On-Time Delivery</h3> | |
| <h2 style="margin: 0.5rem 0;">{:.1f}%</h2> | |
| <p style="color: #28a745; margin: 0;">β° Performance</p> | |
| </div> | |
| """.format(on_time_delivery), unsafe_allow_html=True) | |
| # Charts | |
| st.markdown("### π Executive Dashboard") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| # Spend by category | |
| category_spend = st.session_state.po_df.groupby('material_category')['order_value'].sum().reset_index() | |
| fig_pie = px.pie( | |
| category_spend, | |
| values='order_value', | |
| names='material_category', | |
| title='Spend Distribution by Category', | |
| color_discrete_sequence=px.colors.qualitative.Set3 | |
| ) | |
| fig_pie.update_layout( | |
| title_font_size=16, | |
| title_x=0.5, | |
| showlegend=True, | |
| height=400 | |
| ) | |
| st.plotly_chart(fig_pie, use_container_width=True) | |
| with col2: | |
| # Top vendors | |
| vendor_spend = st.session_state.po_df.groupby('vendor')['order_value'].sum().reset_index() | |
| vendor_spend = vendor_spend.nlargest(8, 'order_value') | |
| fig_bar = px.bar( | |
| vendor_spend, | |
| x='vendor', | |
| y='order_value', | |
| title='Top Vendors by Spend', | |
| color='order_value', | |
| color_continuous_scale='Blues' | |
| ) | |
| fig_bar.update_layout( | |
| title_font_size=16, | |
| title_x=0.5, | |
| xaxis_tickangle=45, | |
| height=400 | |
| ) | |
| st.plotly_chart(fig_bar, use_container_width=True) | |
| elif selected == "π¬ AI Chat": | |
| st.markdown("### π¬ Chat with Your Procurement Data") | |
| st.markdown(f""" | |
| <div class="ai-insight"> | |
| <h4>π€ Intelligent Procurement Assistant</h4> | |
| <p>Ask me anything about your procurement data! I can analyze trends, vendor performance, spending patterns, and provide strategic recommendations.</p> | |
| <p><small>Status: {api_key_status}</small></p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Chat interface | |
| if "messages" not in st.session_state: | |
| st.session_state.messages = [ | |
| {"role": "assistant", "content": "Hello! I'm your AI procurement analyst. I've analyzed your procurement portfolio and I'm ready to help! What would you like to explore?"} | |
| ] | |
| # Display chat messages | |
| for message in st.session_state.messages: | |
| with st.chat_message(message["role"]): | |
| st.markdown(message["content"]) | |
| # Chat input | |
| if prompt := st.chat_input("Ask about your procurement data..."): | |
| # Add user message to chat history | |
| st.session_state.messages.append({"role": "user", "content": prompt}) | |
| with st.chat_message("user"): | |
| st.markdown(prompt) | |
| # Generate AI response | |
| with st.chat_message("assistant"): | |
| with st.spinner("π€ Analyzing your question..."): | |
| response = analytics_agent.chat_with_data(prompt) | |
| st.markdown(response) | |
| # Add assistant response to chat history | |
| st.session_state.messages.append({"role": "assistant", "content": response}) | |
| # Suggested questions | |
| st.markdown("#### π‘ Try these sample questions:") | |
| col1, col2, col3 = st.columns(3) | |
| sample_questions = [ | |
| "What are my biggest spending areas?", | |
| "How are my vendors performing?", | |
| "What risks should I be concerned about?" | |
| ] | |
| for i, (col, question) in enumerate(zip([col1, col2, col3], sample_questions)): | |
| with col: | |
| if st.button(f"π {question}", key=f"q_{i}"): | |
| # Add the question to chat | |
| st.session_state.messages.append({"role": "user", "content": question}) | |
| with st.spinner("π€ Analyzing..."): | |
| response = analytics_agent.chat_with_data(question) | |
| st.session_state.messages.append({"role": "assistant", "content": response}) | |
| st.rerun() | |
| # Clear chat button | |
| if st.button("ποΈ Clear Chat History"): | |
| st.session_state.messages = [ | |
| {"role": "assistant", "content": "Chat cleared! What would you like to know about your procurement data?"} | |
| ] | |
| st.rerun() | |
| elif selected == "π Analytics": | |
| st.markdown("### π Advanced Analytics Dashboard") | |
| # Vendor performance analysis | |
| st.markdown("#### π Vendor Performance Scorecard") | |
| vendor_performance = st.session_state.po_df.groupby('vendor').agg({ | |
| 'order_value': 'sum', | |
| 'on_time_delivery': 'mean', | |
| 'quality_score': 'mean', | |
| 'po_number': 'count' | |
| }).round(2) | |
| vendor_performance.columns = ['Total Spend (β¬)', 'On-Time Delivery (%)', 'Quality Score', 'Order Count'] | |
| vendor_performance['On-Time Delivery (%)'] = (vendor_performance['On-Time Delivery (%)'] * 100).round(1) | |
| vendor_performance = vendor_performance.sort_values('Total Spend (β¬)', ascending=False) | |
| st.dataframe( | |
| vendor_performance.head(10), | |
| use_container_width=True, | |
| column_config={ | |
| "Total Spend (β¬)": st.column_config.NumberColumn( | |
| "Total Spend (β¬)", | |
| help="Total procurement spend with vendor", | |
| format="β¬%.0f", | |
| ), | |
| "On-Time Delivery (%)": st.column_config.NumberColumn( | |
| "On-Time Delivery (%)", | |
| help="Percentage of on-time deliveries", | |
| format="%.1f%%", | |
| ), | |
| "Quality Score": st.column_config.NumberColumn( | |
| "Quality Score", | |
| help="Average quality rating (1-10)", | |
| format="%.1f/10", | |
| ) | |
| } | |
| ) | |
| # Performance charts | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| # Performance scatter plot | |
| fig_scatter = px.scatter( | |
| st.session_state.po_df, | |
| x='on_time_delivery', | |
| y='quality_score', | |
| size='order_value', | |
| color='vendor', | |
| title='Vendor Performance Matrix', | |
| labels={'on_time_delivery': 'On-Time Delivery Rate', 'quality_score': 'Quality Score (1-10)'}, | |
| hover_data=['vendor', 'order_value'] | |
| ) | |
| fig_scatter.update_layout(height=500, showlegend=False) | |
| st.plotly_chart(fig_scatter, use_container_width=True) | |
| with col2: | |
| # Monthly trend | |
| st.session_state.po_df['order_month'] = pd.to_datetime(st.session_state.po_df['order_date']).dt.to_period('M') | |
| monthly_trend = st.session_state.po_df.groupby('order_month')['order_value'].sum().reset_index() | |
| monthly_trend['order_month'] = monthly_trend['order_month'].astype(str) | |
| fig_trend = px.line( | |
| monthly_trend, | |
| x='order_month', | |
| y='order_value', | |
| title='Monthly Procurement Spend Trend', | |
| markers=True | |
| ) | |
| fig_trend.update_layout( | |
| height=500, | |
| xaxis_tickangle=45 | |
| ) | |
| fig_trend.update_traces(line_color='#0066cc', line_width=3, marker_size=8) | |
| st.plotly_chart(fig_trend, use_container_width=True) | |
| elif selected == "π― Recommendations": | |
| st.markdown("### π Strategic Procurement Recommendations") | |
| st.markdown(""" | |
| <div class="ai-insight"> | |
| <h3>π― AI-Powered Strategic Optimization</h3> | |
| <p>Based on comprehensive data analysis, here are prioritized recommendations to enhance your procurement strategy and drive business value.</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Calculate some metrics for recommendations | |
| vendor_count = len(st.session_state.po_df['vendor'].unique()) | |
| avg_order_value = st.session_state.po_df['order_value'].mean() | |
| low_value_orders = len(st.session_state.po_df[st.session_state.po_df['order_value'] < 5000]) | |
| total_orders = len(st.session_state.po_df) | |
| recommendations = [ | |
| { | |
| "priority": "π₯ High Priority", | |
| "title": "Vendor Consolidation Strategy", | |
| "description": f"With {vendor_count} active vendors, consolidating to 5-7 strategic partners could reduce costs by 12-18% and improve relationship management.", | |
| "impact": "π° Cost Reduction: β¬50K-75K annually", | |
| "timeline": "3-6 months" | |
| }, | |
| { | |
| "priority": "β‘ Quick Win", | |
| "title": "Procurement Process Automation", | |
| "description": f"{low_value_orders}/{total_orders} orders ({low_value_orders/total_orders*100:.1f}%) are under β¬5,000. Implementing automated approval workflows could save 40+ hours weekly.", | |
| "impact": "β±οΈ Efficiency Gain: 160 hours/month", | |
| "timeline": "4-8 weeks" | |
| }, | |
| { | |
| "priority": "π Strategic", | |
| "title": "Performance-Based Contracts", | |
| "description": "Implement KPI-driven contracts with top 5 vendors focusing on quality scores >8.5 and delivery performance >90%.", | |
| "impact": "π Performance Improvement: 15-25%", | |
| "timeline": "6-9 months" | |
| }, | |
| { | |
| "priority": "π‘οΈ Risk Management", | |
| "title": "Supplier Risk Monitoring", | |
| "description": "Deploy real-time risk assessment tools to monitor supplier financial health, compliance, and performance metrics.", | |
| "impact": "β οΈ Risk Reduction: 30-40%", | |
| "timeline": "2-4 months" | |
| }, | |
| { | |
| "priority": "π― Innovation", | |
| "title": "Digital Procurement Platform", | |
| "description": "Upgrade to AI-powered procurement platform with predictive analytics, spend optimization, and automated sourcing capabilities.", | |
| "impact": "π Digital Transformation: 25-35% efficiency", | |
| "timeline": "9-12 months" | |
| } | |
| ] | |
| for i, rec in enumerate(recommendations, 1): | |
| priority_color = {"π₯ High Priority": "#dc3545", "β‘ Quick Win": "#28a745", "π Strategic": "#0066cc", "π‘οΈ Risk Management": "#ffc107", "π― Innovation": "#6f42c1"} | |
| st.markdown(f""" | |
| <div class="alert alert-success"> | |
| <h4 style="color: {priority_color[rec['priority']]};">{rec['priority']}</h4> | |
| <h3>{rec['title']}</h3> | |
| <p style="margin-bottom: 1rem;">{rec['description']}</p> | |
| <div style="display: flex; justify-content: space-between; font-size: 0.9rem;"> | |
| <span><strong>{rec['impact']}</strong></span> | |
| <span><strong>β±οΈ Timeline: {rec['timeline']}</strong></span> | |
| </div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Implementation roadmap | |
| st.markdown("#### πΊοΈ Implementation Roadmap") | |
| roadmap_data = { | |
| "Phase": ["Phase 1 (0-3 months)", "Phase 2 (3-6 months)", "Phase 3 (6-12 months)"], | |
| "Focus Areas": [ | |
| "Process Automation, Quick Wins", | |
| "Vendor Consolidation, Risk Management", | |
| "Strategic Contracts, Digital Platform" | |
| ], | |
| "Expected ROI": ["15-20%", "20-30%", "30-40%"], | |
| "Key Deliverables": [ | |
| "Automated workflows, Spend visibility", | |
| "Strategic partnerships, Risk framework", | |
| "AI-powered platform, Performance management" | |
| ] | |
| } | |
| roadmap_df = pd.DataFrame(roadmap_data) | |
| st.dataframe(roadmap_df, use_container_width=True, hide_index=True) | |
| # Footer | |
| st.markdown("---") | |
| st.markdown(f""" | |
| <div style="text-align: center; padding: 1rem; color: #666;"> | |
| <p>π€ <strong>Agentic AI Procurement Analytics</strong> | Built with Streamlit & Python | SAP S/4HANA Integration Demo</p> | |
| <p><em>Synthetic data demonstration β’ {len(st.session_state.po_df):,} orders β’ {len(st.session_state.po_df['vendor'].unique())} vendors β’ OpenAI {api_key_status}</em></p> | |
| <p><small>π‘ Add your OpenAI API key as 'OPENAI_API_KEY' in Hugging Face Space settings for enhanced AI features</small></p> | |
| </div> | |
| """, unsafe_allow_html=True) | |