PD03 commited on
Commit
af1c55a
Β·
verified Β·
1 Parent(s): 45e09fd

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +336 -106
src/streamlit_app.py CHANGED
@@ -3,15 +3,14 @@ import pandas as pd
3
  import numpy as np
4
  import plotly.express as px
5
  import plotly.graph_objects as go
6
- from plotly.subplots import make_subplots
7
  from streamlit_option_menu import option_menu
8
  import time
9
  from faker import Faker
10
  from datetime import datetime, timedelta
11
  import random
12
- import openai
13
  import json
14
  from typing import Dict, List, Any
 
15
 
16
  # Page configuration
17
  st.set_page_config(
@@ -21,7 +20,7 @@ st.set_page_config(
21
  initial_sidebar_state="expanded"
22
  )
23
 
24
- # Custom CSS (same as before)
25
  st.markdown("""
26
  <style>
27
  /* Main theme colors */
@@ -88,10 +87,10 @@ st.markdown("""
88
  color: #856404;
89
  }
90
 
91
- .alert-danger {
92
- background-color: #f8d7da;
93
- border-color: var(--danger-color);
94
- color: #721c24;
95
  }
96
 
97
  /* Button styling */
@@ -112,11 +111,35 @@ st.markdown("""
112
  </style>
113
  """, unsafe_allow_html=True)
114
 
115
- # Data generation function (embedded in main file)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  @st.cache_data
117
  def generate_synthetic_procurement_data():
118
  """Generate synthetic SAP S/4HANA procurement data"""
119
  fake = Faker()
 
 
120
 
121
  # Vendors data
122
  vendors = [
@@ -171,47 +194,64 @@ def generate_synthetic_procurement_data():
171
 
172
  return pd.DataFrame(purchase_orders), pd.DataFrame(spend_data)
173
 
174
- # AI Agent Classes (embedded in main file)
175
  class LLMPoweredProcurementAgent:
176
  """AI Agent powered by OpenAI GPT for intelligent procurement analysis"""
177
 
178
  def __init__(self, po_data: pd.DataFrame, spend_data: pd.DataFrame):
179
  self.po_data = po_data
180
  self.spend_data = spend_data
181
- # Check if OpenAI API key is available
182
- if 'OPENAI_API_KEY' in st.secrets:
183
- self.client = openai.OpenAI(api_key=st.secrets["OPENAI_API_KEY"])
184
- self.llm_available = True
 
 
 
 
 
 
 
 
185
  else:
186
  self.client = None
187
- self.llm_available = False
188
 
189
  def generate_executive_summary(self) -> str:
190
- """Generate an executive summary using GPT"""
191
 
192
  if not self.llm_available:
193
- # Fallback to rule-based summary if no API key
194
  total_spend = self.po_data['order_value'].sum()
195
  total_orders = len(self.po_data)
196
  on_time_rate = self.po_data['on_time_delivery'].mean() * 100
 
 
 
197
 
198
- return f"""
199
- **Executive Summary - Procurement Performance**
200
-
201
- πŸ“Š **Key Metrics**: Processed {total_orders:,} orders worth €{total_spend:,.0f} across {len(self.po_data['vendor'].unique())} vendors.
202
-
203
- 🎯 **Performance Highlights**:
204
- β€’ On-time delivery rate: {on_time_rate:.1f}%
205
- β€’ Average quality score: {self.po_data['quality_score'].mean():.1f}/10
206
- β€’ Top spending category: {self.po_data.groupby('material_category')['order_value'].sum().idxmax()}
207
-
208
- πŸ’‘ **Strategic Recommendations**:
209
- β€’ Focus on vendor consolidation opportunities
210
- β€’ Improve contract compliance monitoring
211
- β€’ Implement performance-based partnerships
212
-
213
- *Note: Connect OpenAI API for enhanced AI insights*
214
- """
 
 
 
 
 
 
 
215
 
216
  # Prepare data summary for LLM
217
  data_summary = {
@@ -226,55 +266,111 @@ class LLMPoweredProcurementAgent:
226
  }
227
 
228
  prompt = f"""
229
- As a senior procurement analyst, provide an executive summary of the procurement performance based on this data:
230
 
231
- {json.dumps(data_summary, indent=2)}
232
 
233
- Please provide:
234
- 1. A concise executive overview (2-3 sentences)
235
- 2. Key performance highlights
236
- 3. Areas of concern (if any)
237
- 4. Strategic recommendations (2-3 bullet points)
238
 
239
- Keep the tone professional and actionable. Focus on business impact.
240
  """
241
 
242
  try:
243
  response = self.client.chat.completions.create(
244
  model="gpt-4",
245
  messages=[
246
- {"role": "system", "content": "You are an expert procurement analyst with 15+ years of experience in SAP S/4HANA systems."},
247
  {"role": "user", "content": prompt}
248
  ],
249
- max_tokens=500,
250
  temperature=0.7
251
  )
252
  return response.choices[0].message.content
253
  except Exception as e:
254
- return f"AI Analysis temporarily unavailable: {str(e)}"
255
 
256
  def chat_with_data(self, user_question: str) -> str:
257
  """Natural language interface to query procurement data"""
258
 
259
  if not self.llm_available:
260
- # Simple rule-based responses
261
  question_lower = user_question.lower()
262
 
263
- if "spend" in question_lower or "cost" in question_lower:
264
  total_spend = self.po_data['order_value'].sum()
265
- return f"Your total procurement spend is €{total_spend:,.0f}. The top spending category is {self.po_data.groupby('material_category')['order_value'].sum().idxmax()}. Connect OpenAI API for detailed analysis."
 
 
 
 
 
 
 
 
 
 
 
266
 
267
- elif "vendor" in question_lower or "supplier" in question_lower:
268
  top_vendor = self.po_data.groupby('vendor')['order_value'].sum().idxmax()
269
- return f"Your top vendor by spend is {top_vendor}. You work with {len(self.po_data['vendor'].unique())} vendors total. Connect OpenAI API for detailed vendor analysis."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
 
271
- elif "risk" in question_lower:
272
- return "Risk analysis shows mixed performance across vendors. Connect OpenAI API for comprehensive risk assessment."
 
 
 
 
 
 
 
 
 
273
 
274
  else:
275
- return "I can help with procurement analysis! Try asking about spending, vendors, or risks. Connect OpenAI API for advanced AI conversation."
 
 
 
 
 
 
 
 
 
 
 
 
276
 
277
- # Create a data context for the LLM
278
  data_context = {
279
  "procurement_summary": {
280
  "total_spend": float(self.po_data['order_value'].sum()),
@@ -297,24 +393,24 @@ class LLMPoweredProcurementAgent:
297
  Procurement Data Context:
298
  {json.dumps(data_context, indent=2)}
299
 
300
- Please answer the user's question based on the procurement data provided. If you need specific calculations or data analysis that isn't available in the context, explain what additional analysis would be needed.
301
-
302
- Keep your response conversational but professional, and always relate back to business impact where possible.
303
  """
304
 
305
  try:
306
  response = self.client.chat.completions.create(
307
  model="gpt-4",
308
  messages=[
309
- {"role": "system", "content": "You are an AI procurement analyst assistant. Answer questions about procurement data in a helpful, conversational way while maintaining professional expertise."},
310
  {"role": "user", "content": prompt}
311
  ],
312
- max_tokens=400,
313
  temperature=0.7
314
  )
315
  return response.choices[0].message.content
316
  except Exception as e:
317
- return f"I'm having trouble accessing the AI right now. Please try again later. Error: {str(e)}"
318
 
319
  def analyze_spend_patterns(self) -> Dict[str, Any]:
320
  """Analyze spending patterns and generate insights"""
@@ -340,7 +436,7 @@ class LLMPoweredProcurementAgent:
340
 
341
  # Initialize session state and data
342
  if 'data_loaded' not in st.session_state:
343
- with st.spinner('πŸ”„ Generating synthetic SAP S/4HANA data...'):
344
  st.session_state.po_df, st.session_state.spend_df = generate_synthetic_procurement_data()
345
  st.session_state.data_loaded = True
346
 
@@ -353,14 +449,15 @@ def initialize_agents():
353
  analytics_agent = initialize_agents()
354
 
355
  # API Key status check
356
- api_key_status = "🟒 Connected" if 'OPENAI_API_KEY' in st.secrets else "πŸ”΄ Not Connected"
 
357
 
358
  # Main header
359
  st.markdown(f"""
360
  <div class="main-header">
361
  <h1>πŸ€– SAP S/4HANA Agentic AI Procurement Analytics</h1>
362
  <p>Autonomous Intelligence for Procurement Excellence</p>
363
- <small>OpenAI Status: {api_key_status}</small>
364
  </div>
365
  """, unsafe_allow_html=True)
366
 
@@ -369,8 +466,15 @@ with st.sidebar:
369
  st.markdown("### πŸ€– AI-Powered Analytics")
370
  st.markdown(f"**OpenAI Status:** {api_key_status}")
371
 
372
- if 'OPENAI_API_KEY' not in st.secrets:
373
- st.warning("⚠️ Add OpenAI API key in Spaces settings for full AI features")
 
 
 
 
 
 
 
374
 
375
  selected = option_menu(
376
  "Navigation",
@@ -385,18 +489,24 @@ with st.sidebar:
385
  "nav-link-selected": {"background-color": "#0066cc"},
386
  }
387
  )
 
 
 
 
 
 
388
 
389
  if selected == "🏠 Dashboard":
390
  # AI-generated insights at the top
391
  st.markdown("### 🧠 AI Executive Summary")
392
 
393
- with st.spinner('πŸ€– AI is analyzing your procurement data...'):
394
  executive_summary = analytics_agent.generate_executive_summary()
395
 
396
  st.markdown(f"""
397
  <div class="ai-insight">
398
  <h4>πŸ“Š Intelligent Analysis</h4>
399
- <div style="white-space: pre-line;">{executive_summary}</div>
400
  </div>
401
  """, unsafe_allow_html=True)
402
 
@@ -410,7 +520,7 @@ if selected == "🏠 Dashboard":
410
  <div class="metric-card">
411
  <h3 style="color: var(--primary-color); margin: 0;">Total Spend</h3>
412
  <h2 style="margin: 0.5rem 0;">€{:,.0f}</h2>
413
- <p style="color: #28a745; margin: 0;">πŸ“ˆ +12% vs last period</p>
414
  </div>
415
  """.format(insights['total_spend']), unsafe_allow_html=True)
416
 
@@ -419,7 +529,7 @@ if selected == "🏠 Dashboard":
419
  <div class="metric-card">
420
  <h3 style="color: var(--primary-color); margin: 0;">Avg Order Value</h3>
421
  <h2 style="margin: 0.5rem 0;">€{:,.0f}</h2>
422
- <p style="color: #ffc107; margin: 0;">πŸ“Š Stable trend</p>
423
  </div>
424
  """.format(insights['avg_order_value']), unsafe_allow_html=True)
425
 
@@ -429,7 +539,7 @@ if selected == "🏠 Dashboard":
429
  <div class="metric-card">
430
  <h3 style="color: var(--primary-color); margin: 0;">Active Vendors</h3>
431
  <h2 style="margin: 0.5rem 0;">{}</h2>
432
- <p style="color: #17a2b8; margin: 0;">🀝 Partnership focus</p>
433
  </div>
434
  """.format(active_vendors), unsafe_allow_html=True)
435
 
@@ -439,7 +549,7 @@ if selected == "🏠 Dashboard":
439
  <div class="metric-card">
440
  <h3 style="color: var(--primary-color); margin: 0;">On-Time Delivery</h3>
441
  <h2 style="margin: 0.5rem 0;">{:.1f}%</h2>
442
- <p style="color: #28a745; margin: 0;">⏰ Excellent performance</p>
443
  </div>
444
  """.format(on_time_delivery), unsafe_allow_html=True)
445
 
@@ -493,7 +603,7 @@ elif selected == "πŸ’¬ AI Chat":
493
  st.markdown(f"""
494
  <div class="ai-insight">
495
  <h4>πŸ€– Intelligent Procurement Assistant</h4>
496
- <p>Ask me anything about your procurement data! I can help you understand trends, analyze vendor performance, identify opportunities, and more.</p>
497
  <p><small>Status: {api_key_status}</small></p>
498
  </div>
499
  """, unsafe_allow_html=True)
@@ -501,7 +611,7 @@ elif selected == "πŸ’¬ AI Chat":
501
  # Chat interface
502
  if "messages" not in st.session_state:
503
  st.session_state.messages = [
504
- {"role": "assistant", "content": "Hello! I'm your AI procurement analyst. What would you like to know about your procurement data?"}
505
  ]
506
 
507
  # Display chat messages
@@ -526,85 +636,205 @@ elif selected == "πŸ’¬ AI Chat":
526
  st.session_state.messages.append({"role": "assistant", "content": response})
527
 
528
  # Suggested questions
529
- st.markdown("#### πŸ’‘ Try asking:")
530
 
531
  col1, col2, col3 = st.columns(3)
532
 
533
  sample_questions = [
534
- "πŸ“Š What are my spending trends?",
535
- "πŸ† Who are my best vendors?",
536
- "⚠️ What risks should I worry about?"
537
  ]
538
 
539
  for i, (col, question) in enumerate(zip([col1, col2, col3], sample_questions)):
540
  with col:
541
- if st.button(question, key=f"q_{i}"):
542
  # Add the question to chat
543
  st.session_state.messages.append({"role": "user", "content": question})
544
  with st.spinner("πŸ€– Analyzing..."):
545
  response = analytics_agent.chat_with_data(question)
546
  st.session_state.messages.append({"role": "assistant", "content": response})
547
  st.rerun()
 
 
 
 
 
 
 
548
 
549
  elif selected == "πŸ“Š Analytics":
550
  st.markdown("### πŸ“ˆ Advanced Analytics Dashboard")
551
 
552
  # Vendor performance analysis
 
 
553
  vendor_performance = st.session_state.po_df.groupby('vendor').agg({
554
  'order_value': 'sum',
555
  'on_time_delivery': 'mean',
556
  'quality_score': 'mean',
557
  'po_number': 'count'
558
  }).round(2)
559
- vendor_performance.columns = ['Total Spend', 'On-Time Delivery', 'Quality Score', 'Order Count']
560
-
561
- st.markdown("#### πŸ† Vendor Performance Analysis")
562
- st.dataframe(vendor_performance.sort_values('Total Spend', ascending=False), use_container_width=True)
563
-
564
- # Performance scatter plot
565
- fig_scatter = px.scatter(
566
- st.session_state.po_df,
567
- x='on_time_delivery',
568
- y='quality_score',
569
- size='order_value',
570
- color='vendor',
571
- title='Vendor Performance: Quality vs Delivery',
572
- labels={'on_time_delivery': 'On-Time Delivery Rate', 'quality_score': 'Quality Score'}
 
 
 
 
 
 
 
 
 
 
573
  )
574
- fig_scatter.update_layout(height=500)
575
- st.plotly_chart(fig_scatter, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
576
 
577
  elif selected == "🎯 Recommendations":
578
- st.markdown("### πŸš€ AI-Generated Strategic Recommendations")
579
 
580
  st.markdown("""
581
  <div class="ai-insight">
582
- <h3>🎯 Strategic Procurement Optimization</h3>
583
- <p>Based on data analysis, here are actionable recommendations to optimize your procurement strategy.</p>
584
  </div>
585
  """, unsafe_allow_html=True)
586
 
 
 
 
 
 
 
587
  recommendations = [
588
- "🎯 **Vendor Consolidation**: Consider consolidating to top 5-7 vendors to reduce costs by 10-15%",
589
- "πŸ“ˆ **Contract Optimization**: Renegotiate contracts with top 3 spending vendors for volume discounts",
590
- "⚑ **Process Automation**: Implement automated PO approval for orders under €5,000",
591
- "πŸ“Š **Performance Monitoring**: Set up real-time dashboards for vendor performance tracking",
592
- "🀝 **Strategic Partnerships**: Develop long-term partnerships with high-performing vendors"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
593
  ]
594
 
595
  for i, rec in enumerate(recommendations, 1):
 
 
596
  st.markdown(f"""
597
  <div class="alert alert-success">
598
- <h4>Recommendation #{i}</h4>
599
- <p>{rec}</p>
 
 
 
 
 
600
  </div>
601
  """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
602
 
603
  # Footer
604
  st.markdown("---")
605
- st.markdown("""
606
  <div style="text-align: center; padding: 1rem; color: #666;">
607
- <p>πŸ€– Powered by Agentic AI | Built with Streamlit | SAP S/4HANA Integration Demo</p>
608
- <p><em>This is a demonstration application with synthetic data for learning purposes</em></p>
 
609
  </div>
610
  """, unsafe_allow_html=True)
 
3
  import numpy as np
4
  import plotly.express as px
5
  import plotly.graph_objects as go
 
6
  from streamlit_option_menu import option_menu
7
  import time
8
  from faker import Faker
9
  from datetime import datetime, timedelta
10
  import random
 
11
  import json
12
  from typing import Dict, List, Any
13
+ import os
14
 
15
  # Page configuration
16
  st.set_page_config(
 
20
  initial_sidebar_state="expanded"
21
  )
22
 
23
+ # Custom CSS
24
  st.markdown("""
25
  <style>
26
  /* Main theme colors */
 
87
  color: #856404;
88
  }
89
 
90
+ .alert-info {
91
+ background-color: #d1ecf1;
92
+ border-color: #17a2b8;
93
+ color: #0c5460;
94
  }
95
 
96
  /* Button styling */
 
111
  </style>
112
  """, unsafe_allow_html=True)
113
 
114
+ # Function to safely get OpenAI API key
115
+ def get_openai_api_key():
116
+ """Safely retrieve OpenAI API key from various sources"""
117
+ api_key = None
118
+
119
+ # Method 1: Try from Streamlit secrets
120
+ try:
121
+ if hasattr(st, 'secrets') and 'OPENAI_API_KEY' in st.secrets:
122
+ api_key = st.secrets["OPENAI_API_KEY"]
123
+ except Exception:
124
+ pass
125
+
126
+ # Method 2: Try from environment variables
127
+ if not api_key:
128
+ api_key = os.getenv('OPENAI_API_KEY')
129
+
130
+ # Method 3: Try from Hugging Face Spaces environment
131
+ if not api_key:
132
+ api_key = os.getenv('OPENAI_API_TOKEN') # Sometimes HF uses this
133
+
134
+ return api_key
135
+
136
+ # Data generation function
137
  @st.cache_data
138
  def generate_synthetic_procurement_data():
139
  """Generate synthetic SAP S/4HANA procurement data"""
140
  fake = Faker()
141
+ np.random.seed(42) # For reproducible data
142
+ random.seed(42)
143
 
144
  # Vendors data
145
  vendors = [
 
194
 
195
  return pd.DataFrame(purchase_orders), pd.DataFrame(spend_data)
196
 
197
+ # AI Agent Classes
198
  class LLMPoweredProcurementAgent:
199
  """AI Agent powered by OpenAI GPT for intelligent procurement analysis"""
200
 
201
  def __init__(self, po_data: pd.DataFrame, spend_data: pd.DataFrame):
202
  self.po_data = po_data
203
  self.spend_data = spend_data
204
+
205
+ # Safely get OpenAI API key
206
+ self.api_key = get_openai_api_key()
207
+ self.llm_available = bool(self.api_key)
208
+
209
+ if self.llm_available:
210
+ try:
211
+ import openai
212
+ self.client = openai.OpenAI(api_key=self.api_key)
213
+ except ImportError:
214
+ self.llm_available = False
215
+ self.client = None
216
  else:
217
  self.client = None
 
218
 
219
  def generate_executive_summary(self) -> str:
220
+ """Generate an executive summary using GPT or fallback"""
221
 
222
  if not self.llm_available:
223
+ # Enhanced rule-based summary if no API key
224
  total_spend = self.po_data['order_value'].sum()
225
  total_orders = len(self.po_data)
226
  on_time_rate = self.po_data['on_time_delivery'].mean() * 100
227
+ quality_avg = self.po_data['quality_score'].mean()
228
+ top_category = self.po_data.groupby('material_category')['order_value'].sum().idxmax()
229
+ top_vendor = self.po_data.groupby('vendor')['order_value'].sum().idxmax()
230
 
231
+ return f"""**🎯 Executive Summary - Procurement Performance Dashboard**
232
+
233
+ πŸ“Š **Current Portfolio Overview**
234
+ β€’ Total procurement spend: €{total_spend:,.0f} across {total_orders:,} purchase orders
235
+ β€’ Active vendor network: {len(self.po_data['vendor'].unique())} strategic suppliers
236
+ β€’ Average order value: €{self.po_data['order_value'].mean():,.0f}
237
+
238
+ πŸ† **Performance Highlights**
239
+ β€’ On-time delivery performance: {on_time_rate:.1f}% (Industry benchmark: 85%)
240
+ β€’ Average supplier quality score: {quality_avg:.1f}/10
241
+ β€’ Leading spend category: {top_category}
242
+ β€’ Top strategic partner: {top_vendor}
243
+
244
+ ⚑ **Strategic Opportunities**
245
+ β€’ Vendor consolidation potential identified in {len(self.po_data['vendor'].unique())} supplier base
246
+ β€’ Contract optimization opportunities with top-tier vendors
247
+ β€’ Digital procurement automation possibilities for routine purchases
248
+
249
+ πŸ’‘ **AI-Powered Recommendations**
250
+ β€’ Implement strategic sourcing for {top_category} category
251
+ β€’ Develop performance-based contracts with high-performing suppliers
252
+ β€’ Establish automated approval workflows for orders under €10,000
253
+
254
+ *πŸ”§ Note: Connect OpenAI API for advanced AI insights and natural language analysis*"""
255
 
256
  # Prepare data summary for LLM
257
  data_summary = {
 
266
  }
267
 
268
  prompt = f"""
269
+ As a senior procurement analyst with expertise in SAP S/4HANA systems, provide an executive summary of procurement performance:
270
 
271
+ Data: {json.dumps(data_summary, indent=2)}
272
 
273
+ Provide:
274
+ 1. Executive overview (2-3 sentences)
275
+ 2. Key performance highlights with specific metrics
276
+ 3. Critical areas needing attention
277
+ 4. Strategic recommendations (3-4 actionable items)
278
 
279
+ Keep it professional, metrics-focused, and actionable for C-level executives.
280
  """
281
 
282
  try:
283
  response = self.client.chat.completions.create(
284
  model="gpt-4",
285
  messages=[
286
+ {"role": "system", "content": "You are a senior procurement analyst with 15+ years of SAP S/4HANA experience."},
287
  {"role": "user", "content": prompt}
288
  ],
289
+ max_tokens=600,
290
  temperature=0.7
291
  )
292
  return response.choices[0].message.content
293
  except Exception as e:
294
+ return f"πŸ€– AI Analysis temporarily unavailable. Using rule-based insights instead.\n\n{self.generate_executive_summary()}"
295
 
296
  def chat_with_data(self, user_question: str) -> str:
297
  """Natural language interface to query procurement data"""
298
 
299
  if not self.llm_available:
300
+ # Enhanced rule-based responses
301
  question_lower = user_question.lower()
302
 
303
+ if any(word in question_lower for word in ["spend", "cost", "money", "budget"]):
304
  total_spend = self.po_data['order_value'].sum()
305
+ top_category = self.po_data.groupby('material_category')['order_value'].sum().idxmax()
306
+ monthly_avg = total_spend / 24 # Assuming 2 years of data
307
+ return f"""πŸ’° **Spend Analysis:**
308
+
309
+ β€’ **Total procurement spend**: €{total_spend:,.0f}
310
+ β€’ **Monthly average**: €{monthly_avg:,.0f}
311
+ β€’ **Largest spend category**: {top_category}
312
+ β€’ **Average order size**: €{self.po_data['order_value'].mean():,.0f}
313
+
314
+ The spending is distributed across {len(self.po_data['material_category'].unique())} categories with {top_category} representing the highest investment area.
315
+
316
+ *πŸ’‘ Connect OpenAI API for detailed spend optimization strategies!*"""
317
 
318
+ elif any(word in question_lower for word in ["vendor", "supplier", "partner"]):
319
  top_vendor = self.po_data.groupby('vendor')['order_value'].sum().idxmax()
320
+ vendor_count = len(self.po_data['vendor'].unique())
321
+ top_vendor_performance = self.po_data[self.po_data['vendor'] == top_vendor]['on_time_delivery'].mean() * 100
322
+ return f"""🀝 **Vendor Analysis:**
323
+
324
+ β€’ **Total active vendors**: {vendor_count}
325
+ β€’ **Top strategic partner**: {top_vendor}
326
+ β€’ **{top_vendor} performance**: {top_vendor_performance:.1f}% on-time delivery
327
+ β€’ **Vendor diversity**: Well-distributed across multiple suppliers
328
+
329
+ Your vendor portfolio shows good diversification with {top_vendor} as the leading partner.
330
+
331
+ *πŸ’‘ Connect OpenAI API for detailed vendor relationship strategies!*"""
332
+
333
+ elif any(word in question_lower for word in ["risk", "compliance", "quality"]):
334
+ avg_quality = self.po_data['quality_score'].mean()
335
+ on_time_rate = self.po_data['on_time_delivery'].mean() * 100
336
+ return f"""⚠️ **Risk & Quality Analysis:**
337
+
338
+ β€’ **Average quality score**: {avg_quality:.1f}/10
339
+ β€’ **On-time delivery rate**: {on_time_rate:.1f}%
340
+ β€’ **Performance status**: {'Excellent' if avg_quality > 8.5 else 'Good' if avg_quality > 7.5 else 'Needs Improvement'}
341
+
342
+ Overall risk profile appears {'low' if on_time_rate > 85 else 'moderate'} based on delivery performance metrics.
343
+
344
+ *πŸ’‘ Connect OpenAI API for comprehensive risk assessment!*"""
345
 
346
+ elif any(word in question_lower for word in ["trend", "pattern", "analysis"]):
347
+ return f"""πŸ“ˆ **Trend Analysis:**
348
+
349
+ β€’ **Data period**: {self.po_data['order_date'].min()} to {self.po_data['order_date'].max()}
350
+ β€’ **Total orders processed**: {len(self.po_data):,}
351
+ β€’ **Peak category**: {self.po_data.groupby('material_category')['order_value'].sum().idxmax()}
352
+ β€’ **Seasonal patterns**: Data shows consistent procurement activity
353
+
354
+ Historical data indicates stable procurement operations with opportunities for optimization.
355
+
356
+ *πŸ’‘ Connect OpenAI API for advanced trend forecasting!*"""
357
 
358
  else:
359
+ return f"""πŸ€– **Procurement Assistant Ready!**
360
+
361
+ I can help you analyze:
362
+ β€’ πŸ’° **Spending patterns** and budget optimization
363
+ β€’ 🀝 **Vendor performance** and relationship management
364
+ β€’ ⚠️ **Risk assessment** and quality metrics
365
+ β€’ πŸ“ˆ **Trends and forecasting** for strategic planning
366
+
367
+ **Current data scope**: {len(self.po_data):,} orders across {len(self.po_data['vendor'].unique())} vendors
368
+
369
+ Try asking: "What are my biggest spending areas?" or "How are my vendors performing?"
370
+
371
+ *πŸ’‘ Connect OpenAI API for natural language conversations and advanced insights!*"""
372
 
373
+ # LLM-powered response
374
  data_context = {
375
  "procurement_summary": {
376
  "total_spend": float(self.po_data['order_value'].sum()),
 
393
  Procurement Data Context:
394
  {json.dumps(data_context, indent=2)}
395
 
396
+ Answer the user's question based on the procurement data. Be conversational yet professional.
397
+ Include specific metrics when relevant and relate findings to business impact.
398
+ If you need additional data not available in the context, suggest what analysis would be helpful.
399
  """
400
 
401
  try:
402
  response = self.client.chat.completions.create(
403
  model="gpt-4",
404
  messages=[
405
+ {"role": "system", "content": "You are an expert procurement analyst assistant. Provide helpful, professional responses about procurement data and strategy."},
406
  {"role": "user", "content": prompt}
407
  ],
408
+ max_tokens=500,
409
  temperature=0.7
410
  )
411
  return response.choices[0].message.content
412
  except Exception as e:
413
+ 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)}"
414
 
415
  def analyze_spend_patterns(self) -> Dict[str, Any]:
416
  """Analyze spending patterns and generate insights"""
 
436
 
437
  # Initialize session state and data
438
  if 'data_loaded' not in st.session_state:
439
+ with st.spinner('πŸ”„ Generating synthetic SAP S/4HANA procurement data...'):
440
  st.session_state.po_df, st.session_state.spend_df = generate_synthetic_procurement_data()
441
  st.session_state.data_loaded = True
442
 
 
449
  analytics_agent = initialize_agents()
450
 
451
  # API Key status check
452
+ api_key = get_openai_api_key()
453
+ api_key_status = "🟒 Connected" if api_key else "πŸ”΄ Not Connected"
454
 
455
  # Main header
456
  st.markdown(f"""
457
  <div class="main-header">
458
  <h1>πŸ€– SAP S/4HANA Agentic AI Procurement Analytics</h1>
459
  <p>Autonomous Intelligence for Procurement Excellence</p>
460
+ <small>OpenAI Status: {api_key_status} | Data: {len(st.session_state.po_df):,} Purchase Orders</small>
461
  </div>
462
  """, unsafe_allow_html=True)
463
 
 
466
  st.markdown("### πŸ€– AI-Powered Analytics")
467
  st.markdown(f"**OpenAI Status:** {api_key_status}")
468
 
469
+ if not api_key:
470
+ st.markdown("""
471
+ <div class="alert alert-info">
472
+ <small><strong>πŸ’‘ Enhanced AI Features</strong><br>
473
+ Add OpenAI API key as OPENAI_API_KEY in your Hugging Face Space settings for advanced AI conversations and insights!</small>
474
+ </div>
475
+ """, unsafe_allow_html=True)
476
+
477
+ st.markdown("---")
478
 
479
  selected = option_menu(
480
  "Navigation",
 
489
  "nav-link-selected": {"background-color": "#0066cc"},
490
  }
491
  )
492
+
493
+ st.markdown("---")
494
+ st.markdown("### πŸ“Š Quick Stats")
495
+ st.metric("Total Orders", f"{len(st.session_state.po_df):,}")
496
+ st.metric("Active Vendors", f"{len(st.session_state.po_df['vendor'].unique())}")
497
+ st.metric("Categories", f"{len(st.session_state.po_df['material_category'].unique())}")
498
 
499
  if selected == "🏠 Dashboard":
500
  # AI-generated insights at the top
501
  st.markdown("### 🧠 AI Executive Summary")
502
 
503
+ with st.spinner('πŸ€– AI analyzing procurement data...'):
504
  executive_summary = analytics_agent.generate_executive_summary()
505
 
506
  st.markdown(f"""
507
  <div class="ai-insight">
508
  <h4>πŸ“Š Intelligent Analysis</h4>
509
+ <div style="white-space: pre-line; line-height: 1.6;">{executive_summary}</div>
510
  </div>
511
  """, unsafe_allow_html=True)
512
 
 
520
  <div class="metric-card">
521
  <h3 style="color: var(--primary-color); margin: 0;">Total Spend</h3>
522
  <h2 style="margin: 0.5rem 0;">€{:,.0f}</h2>
523
+ <p style="color: #28a745; margin: 0;">πŸ“ˆ Active Portfolio</p>
524
  </div>
525
  """.format(insights['total_spend']), unsafe_allow_html=True)
526
 
 
529
  <div class="metric-card">
530
  <h3 style="color: var(--primary-color); margin: 0;">Avg Order Value</h3>
531
  <h2 style="margin: 0.5rem 0;">€{:,.0f}</h2>
532
+ <p style="color: #17a2b8; margin: 0;">πŸ“Š Order Efficiency</p>
533
  </div>
534
  """.format(insights['avg_order_value']), unsafe_allow_html=True)
535
 
 
539
  <div class="metric-card">
540
  <h3 style="color: var(--primary-color); margin: 0;">Active Vendors</h3>
541
  <h2 style="margin: 0.5rem 0;">{}</h2>
542
+ <p style="color: #6f42c1; margin: 0;">🀝 Strategic Partners</p>
543
  </div>
544
  """.format(active_vendors), unsafe_allow_html=True)
545
 
 
549
  <div class="metric-card">
550
  <h3 style="color: var(--primary-color); margin: 0;">On-Time Delivery</h3>
551
  <h2 style="margin: 0.5rem 0;">{:.1f}%</h2>
552
+ <p style="color: #28a745; margin: 0;">⏰ Performance</p>
553
  </div>
554
  """.format(on_time_delivery), unsafe_allow_html=True)
555
 
 
603
  st.markdown(f"""
604
  <div class="ai-insight">
605
  <h4>πŸ€– Intelligent Procurement Assistant</h4>
606
+ <p>Ask me anything about your procurement data! I can analyze trends, vendor performance, spending patterns, and provide strategic recommendations.</p>
607
  <p><small>Status: {api_key_status}</small></p>
608
  </div>
609
  """, unsafe_allow_html=True)
 
611
  # Chat interface
612
  if "messages" not in st.session_state:
613
  st.session_state.messages = [
614
+ {"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?"}
615
  ]
616
 
617
  # Display chat messages
 
636
  st.session_state.messages.append({"role": "assistant", "content": response})
637
 
638
  # Suggested questions
639
+ st.markdown("#### πŸ’‘ Try these sample questions:")
640
 
641
  col1, col2, col3 = st.columns(3)
642
 
643
  sample_questions = [
644
+ "What are my biggest spending areas?",
645
+ "How are my vendors performing?",
646
+ "What risks should I be concerned about?"
647
  ]
648
 
649
  for i, (col, question) in enumerate(zip([col1, col2, col3], sample_questions)):
650
  with col:
651
+ if st.button(f"πŸ’­ {question}", key=f"q_{i}"):
652
  # Add the question to chat
653
  st.session_state.messages.append({"role": "user", "content": question})
654
  with st.spinner("πŸ€– Analyzing..."):
655
  response = analytics_agent.chat_with_data(question)
656
  st.session_state.messages.append({"role": "assistant", "content": response})
657
  st.rerun()
658
+
659
+ # Clear chat button
660
+ if st.button("πŸ—‘οΈ Clear Chat History"):
661
+ st.session_state.messages = [
662
+ {"role": "assistant", "content": "Chat cleared! What would you like to know about your procurement data?"}
663
+ ]
664
+ st.rerun()
665
 
666
  elif selected == "πŸ“Š Analytics":
667
  st.markdown("### πŸ“ˆ Advanced Analytics Dashboard")
668
 
669
  # Vendor performance analysis
670
+ st.markdown("#### πŸ† Vendor Performance Scorecard")
671
+
672
  vendor_performance = st.session_state.po_df.groupby('vendor').agg({
673
  'order_value': 'sum',
674
  'on_time_delivery': 'mean',
675
  'quality_score': 'mean',
676
  'po_number': 'count'
677
  }).round(2)
678
+ vendor_performance.columns = ['Total Spend (€)', 'On-Time Delivery (%)', 'Quality Score', 'Order Count']
679
+ vendor_performance['On-Time Delivery (%)'] = (vendor_performance['On-Time Delivery (%)'] * 100).round(1)
680
+ vendor_performance = vendor_performance.sort_values('Total Spend (€)', ascending=False)
681
+
682
+ st.dataframe(
683
+ vendor_performance.head(10),
684
+ use_container_width=True,
685
+ column_config={
686
+ "Total Spend (€)": st.column_config.NumberColumn(
687
+ "Total Spend (€)",
688
+ help="Total procurement spend with vendor",
689
+ format="€%.0f",
690
+ ),
691
+ "On-Time Delivery (%)": st.column_config.NumberColumn(
692
+ "On-Time Delivery (%)",
693
+ help="Percentage of on-time deliveries",
694
+ format="%.1f%%",
695
+ ),
696
+ "Quality Score": st.column_config.NumberColumn(
697
+ "Quality Score",
698
+ help="Average quality rating (1-10)",
699
+ format="%.1f/10",
700
+ )
701
+ }
702
  )
703
+
704
+ # Performance charts
705
+ col1, col2 = st.columns(2)
706
+
707
+ with col1:
708
+ # Performance scatter plot
709
+ fig_scatter = px.scatter(
710
+ st.session_state.po_df,
711
+ x='on_time_delivery',
712
+ y='quality_score',
713
+ size='order_value',
714
+ color='vendor',
715
+ title='Vendor Performance Matrix',
716
+ labels={'on_time_delivery': 'On-Time Delivery Rate', 'quality_score': 'Quality Score (1-10)'},
717
+ hover_data=['vendor', 'order_value']
718
+ )
719
+ fig_scatter.update_layout(height=500, showlegend=False)
720
+ st.plotly_chart(fig_scatter, use_container_width=True)
721
+
722
+ with col2:
723
+ # Monthly trend
724
+ st.session_state.po_df['order_month'] = pd.to_datetime(st.session_state.po_df['order_date']).dt.to_period('M')
725
+ monthly_trend = st.session_state.po_df.groupby('order_month')['order_value'].sum().reset_index()
726
+ monthly_trend['order_month'] = monthly_trend['order_month'].astype(str)
727
+
728
+ fig_trend = px.line(
729
+ monthly_trend,
730
+ x='order_month',
731
+ y='order_value',
732
+ title='Monthly Procurement Spend Trend',
733
+ markers=True
734
+ )
735
+ fig_trend.update_layout(
736
+ height=500,
737
+ xaxis_tickangle=45
738
+ )
739
+ fig_trend.update_traces(line_color='#0066cc', line_width=3, marker_size=8)
740
+ st.plotly_chart(fig_trend, use_container_width=True)
741
 
742
  elif selected == "🎯 Recommendations":
743
+ st.markdown("### πŸš€ Strategic Procurement Recommendations")
744
 
745
  st.markdown("""
746
  <div class="ai-insight">
747
+ <h3>🎯 AI-Powered Strategic Optimization</h3>
748
+ <p>Based on comprehensive data analysis, here are prioritized recommendations to enhance your procurement strategy and drive business value.</p>
749
  </div>
750
  """, unsafe_allow_html=True)
751
 
752
+ # Calculate some metrics for recommendations
753
+ vendor_count = len(st.session_state.po_df['vendor'].unique())
754
+ avg_order_value = st.session_state.po_df['order_value'].mean()
755
+ low_value_orders = len(st.session_state.po_df[st.session_state.po_df['order_value'] < 5000])
756
+ total_orders = len(st.session_state.po_df)
757
+
758
  recommendations = [
759
+ {
760
+ "priority": "πŸ”₯ High Priority",
761
+ "title": "Vendor Consolidation Strategy",
762
+ "description": f"With {vendor_count} active vendors, consolidating to 5-7 strategic partners could reduce costs by 12-18% and improve relationship management.",
763
+ "impact": "πŸ’° Cost Reduction: €50K-75K annually",
764
+ "timeline": "3-6 months"
765
+ },
766
+ {
767
+ "priority": "⚑ Quick Win",
768
+ "title": "Procurement Process Automation",
769
+ "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.",
770
+ "impact": "⏱️ Efficiency Gain: 160 hours/month",
771
+ "timeline": "4-8 weeks"
772
+ },
773
+ {
774
+ "priority": "πŸ“ˆ Strategic",
775
+ "title": "Performance-Based Contracts",
776
+ "description": "Implement KPI-driven contracts with top 5 vendors focusing on quality scores >8.5 and delivery performance >90%.",
777
+ "impact": "πŸ“Š Performance Improvement: 15-25%",
778
+ "timeline": "6-9 months"
779
+ },
780
+ {
781
+ "priority": "πŸ›‘οΈ Risk Management",
782
+ "title": "Supplier Risk Monitoring",
783
+ "description": "Deploy real-time risk assessment tools to monitor supplier financial health, compliance, and performance metrics.",
784
+ "impact": "⚠️ Risk Reduction: 30-40%",
785
+ "timeline": "2-4 months"
786
+ },
787
+ {
788
+ "priority": "🎯 Innovation",
789
+ "title": "Digital Procurement Platform",
790
+ "description": "Upgrade to AI-powered procurement platform with predictive analytics, spend optimization, and automated sourcing capabilities.",
791
+ "impact": "πŸš€ Digital Transformation: 25-35% efficiency",
792
+ "timeline": "9-12 months"
793
+ }
794
  ]
795
 
796
  for i, rec in enumerate(recommendations, 1):
797
+ priority_color = {"πŸ”₯ High Priority": "#dc3545", "⚑ Quick Win": "#28a745", "πŸ“ˆ Strategic": "#0066cc", "πŸ›‘οΈ Risk Management": "#ffc107", "🎯 Innovation": "#6f42c1"}
798
+
799
  st.markdown(f"""
800
  <div class="alert alert-success">
801
+ <h4 style="color: {priority_color[rec['priority']]};">{rec['priority']}</h4>
802
+ <h3>{rec['title']}</h3>
803
+ <p style="margin-bottom: 1rem;">{rec['description']}</p>
804
+ <div style="display: flex; justify-content: space-between; font-size: 0.9rem;">
805
+ <span><strong>{rec['impact']}</strong></span>
806
+ <span><strong>⏱️ Timeline: {rec['timeline']}</strong></span>
807
+ </div>
808
  </div>
809
  """, unsafe_allow_html=True)
810
+
811
+ # Implementation roadmap
812
+ st.markdown("#### πŸ—ΊοΈ Implementation Roadmap")
813
+
814
+ roadmap_data = {
815
+ "Phase": ["Phase 1 (0-3 months)", "Phase 2 (3-6 months)", "Phase 3 (6-12 months)"],
816
+ "Focus Areas": [
817
+ "Process Automation, Quick Wins",
818
+ "Vendor Consolidation, Risk Management",
819
+ "Strategic Contracts, Digital Platform"
820
+ ],
821
+ "Expected ROI": ["15-20%", "20-30%", "30-40%"],
822
+ "Key Deliverables": [
823
+ "Automated workflows, Spend visibility",
824
+ "Strategic partnerships, Risk framework",
825
+ "AI-powered platform, Performance management"
826
+ ]
827
+ }
828
+
829
+ roadmap_df = pd.DataFrame(roadmap_data)
830
+ st.dataframe(roadmap_df, use_container_width=True, hide_index=True)
831
 
832
  # Footer
833
  st.markdown("---")
834
+ st.markdown(f"""
835
  <div style="text-align: center; padding: 1rem; color: #666;">
836
+ <p>πŸ€– <strong>Agentic AI Procurement Analytics</strong> | Built with Streamlit & Python | SAP S/4HANA Integration Demo</p>
837
+ <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>
838
+ <p><small>πŸ’‘ Add your OpenAI API key as 'OPENAI_API_KEY' in Hugging Face Space settings for enhanced AI features</small></p>
839
  </div>
840
  """, unsafe_allow_html=True)