PD03 commited on
Commit
4d94e38
Β·
verified Β·
1 Parent(s): b4ebd81

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +361 -197
app.py CHANGED
@@ -1,256 +1,420 @@
1
  import streamlit as st
2
- import json
3
  import pandas as pd
 
4
  import plotly.express as px
5
  import plotly.graph_objects as go
6
- from datetime import datetime
 
7
  import time
8
- from agent import ProcurementRLAgent
9
- from tools import (
10
- InventoryTool, ExternalRiskTool, ContractTool,
11
- POExecutionTool, HumanNotificationTool
 
 
 
 
 
 
12
  )
13
 
14
- # Page configuration
15
  st.set_page_config(
16
- page_title="Agentic AI Procurement Demo",
17
  page_icon="πŸ€–",
18
  layout="wide",
19
  initial_sidebar_state="expanded"
20
  )
21
 
22
- # Custom CSS for better styling
23
  st.markdown("""
24
  <style>
25
  .main-header {
26
- font-size: 2.5rem;
27
- color: #1f77b4;
 
28
  text-align: center;
29
  margin-bottom: 2rem;
30
  }
31
- .decision-box {
32
- background-color: #f0f8ff;
33
- border-left: 5px solid #1f77b4;
 
 
 
 
34
  padding: 1rem;
35
- margin: 1rem 0;
 
36
  }
37
- .agent-thinking {
38
- background-color: #fff3cd;
39
- border: 1px solid #ffeaa7;
40
  padding: 1rem;
 
41
  border-radius: 5px;
42
  }
43
  .success-box {
44
- background-color: #d4edda;
45
- border-left: 5px solid #28a745;
46
  padding: 1rem;
 
47
  margin: 1rem 0;
48
  }
49
  .warning-box {
50
- background-color: #f8d7da;
51
- border-left: 5px solid #dc3545;
52
  padding: 1rem;
 
53
  margin: 1rem 0;
54
  }
55
  </style>
56
  """, unsafe_allow_html=True)
57
 
58
- # Initialize tools
59
- @st.cache_resource
60
- def initialize_agent():
61
- tools = [
62
- InventoryTool(),
63
- ExternalRiskTool(),
64
- ContractTool(),
65
- POExecutionTool(),
66
- HumanNotificationTool()
67
- ]
68
- return ProcurementRLAgent(tools=tools)
69
-
70
- agent = initialize_agent()
 
 
 
 
 
 
 
 
 
71
 
72
- # Title
73
- st.markdown('<h1 class="main-header">πŸ€– Agentic AI Procurement Assistant</h1>', unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
74
 
75
- # Sidebar for demo controls
76
- with st.sidebar:
77
- st.header("Demo Controls")
78
 
79
- # Sample scenarios
80
- scenarios = {
81
- "Standard Office Supplies": {
82
- "PR_ID": "PR-12345",
83
- "Item": "Ergonomic Office Chairs",
84
- "Category": "Office Supplies",
85
- "Quantity": 25,
86
- "Urgency": "Medium",
87
- "Budget": 12500.0,
88
- "Current_Inventory": 5,
89
- "Contract_Status": "Valid",
90
- "External_Disruption": False,
91
- "Supplier_History": "Good"
92
- },
93
- "Urgent IT Equipment": {
94
- "PR_ID": "PR-67890",
95
- "Item": "Enterprise Laptops",
96
- "Category": "IT Equipment",
97
- "Quantity": 50,
98
- "Urgency": "High",
99
- "Budget": 75000.0,
100
- "Current_Inventory": 10,
101
- "Contract_Status": "Expired",
102
- "External_Disruption": False,
103
- "Supplier_History": "Excellent"
104
- },
105
- "Disrupted Raw Materials": {
106
- "PR_ID": "PR-11111",
107
- "Item": "Steel Components",
108
- "Category": "Raw Materials",
109
- "Quantity": 100,
110
- "Urgency": "High",
111
- "Budget": 50000.0,
112
- "Current_Inventory": 0,
113
- "Contract_Status": "Valid",
114
- "External_Disruption": True,
115
- "Supplier_History": "Average"
116
- }
117
- }
118
 
119
- selected_scenario = st.selectbox("Select Demo Scenario:", list(scenarios.keys()))
120
- pr_data = scenarios[selected_scenario]
121
 
122
- st.header("Model Performance")
123
 
124
- # Load historical data for metrics
125
- try:
126
- with open("demo_space/historical_procurement_data.json", "r") as f:
127
- historical_data = json.load(f)
 
 
 
 
128
 
129
- df = pd.DataFrame(historical_data)
130
- avg_delivery = df['delivery_performance'].mean()
131
- avg_quality = df['quality_score'].mean()
132
-
133
- st.metric("Avg Delivery Performance", f"{avg_delivery:.2%}")
134
- st.metric("Avg Quality Score", f"{avg_quality:.2%}")
135
- st.metric("Training Samples", len(historical_data))
136
- except FileNotFoundError:
137
- st.warning("Historical data not found")
138
-
139
- # Main content area
140
- col1, col2 = st.columns([1, 1])
141
-
142
- with col1:
143
- st.subheader("πŸ“‹ Purchase Requisition Details")
144
-
145
- # Display PR information in a nice format
146
- pr_display = {
147
- "PR ID": pr_data["PR_ID"],
148
- "Item": pr_data["Item"],
149
- "Category": pr_data["Category"],
150
- "Quantity": pr_data["Quantity"],
151
- "Urgency": pr_data["Urgency"],
152
- "Budget": f"${pr_data['Budget']:,.2f}",
153
- "Current Inventory": pr_data["Current_Inventory"],
154
- "Contract Status": pr_data["Contract_Status"],
155
- "External Disruption": "⚠️ Yes" if pr_data["External_Disruption"] else "βœ… No",
156
- "Supplier History": pr_data["Supplier_History"]
157
- }
158
 
159
- for key, value in pr_display.items():
160
- st.write(f"**{key}:** {value}")
 
 
 
 
 
 
 
161
 
162
- with col2:
163
- st.subheader("🎯 Agent Decision Engine")
 
 
164
 
165
- if st.button("πŸš€ Execute Agent Decision", type="primary"):
166
- # Show thinking process
167
- with st.spinner("Agent analyzing procurement request..."):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  progress_bar = st.progress(0)
169
  status_text = st.empty()
170
 
171
- # Simulate agent thinking process
172
- thinking_steps = [
173
- "Analyzing purchase requisition...",
174
- "Checking inventory levels...",
175
- "Assessing external risks...",
176
- "Validating contract status...",
177
- "Computing optimal action...",
178
- "Preparing recommendation..."
179
- ]
 
 
 
 
 
 
 
 
180
 
181
- for i, step in enumerate(thinking_steps):
182
- status_text.text(step)
183
- progress_bar.progress((i + 1) / len(thinking_steps))
184
- time.sleep(0.5)
 
 
 
 
 
 
 
185
 
186
- # Get agent decision
187
- decision_result = agent.decide(pr_data)
 
188
 
189
- # Clear progress indicators
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  progress_bar.empty()
191
  status_text.empty()
192
-
193
- # Display decision
194
- if decision_result["action_type"] == "AUTO_EXECUTE":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  st.markdown(f"""
196
  <div class="success-box">
197
- <h4>βœ… Automatic Execution</h4>
198
- <p><strong>Action:</strong> {decision_result['action']}</p>
199
- <p><strong>Reason:</strong> {decision_result['reason']}</p>
200
- <p><strong>Confidence:</strong> {decision_result['confidence']:.1%}</p>
201
  </div>
202
  """, unsafe_allow_html=True)
203
- else:
204
- st.markdown(f"""
205
- <div class="warning-box">
206
- <h4>⚠️ Human Review Required</h4>
207
- <p><strong>Issue:</strong> {decision_result['action']}</p>
208
- <p><strong>Reason:</strong> {decision_result['reason']}</p>
209
- <p><strong>Recommended Action:</strong> {decision_result.get('recommendation', 'Review and approve manually')}</p>
210
- </div>
211
- """, unsafe_allow_html=True)
212
-
213
- # Show tool execution logs
214
- st.subheader("πŸ” Agent Analysis Log")
215
- for log_entry in decision_result["logs"]:
216
- st.text(f"[{log_entry['timestamp']}] {log_entry['tool']}: {log_entry['result']}")
217
-
218
- # Performance Dashboard
219
- st.subheader("πŸ“Š Model Performance Dashboard")
220
-
221
- col3, col4, col5 = st.columns(3)
222
-
223
- try:
224
- with open("demo_space/historical_procurement_data.json", "r") as f:
225
- historical_data = json.load(f)
226
-
227
- df = pd.DataFrame(historical_data)
228
-
229
- with col3:
230
- # Delivery performance by urgency
231
- delivery_by_urgency = df.groupby('urgency')['delivery_performance'].mean().reset_index()
232
- fig1 = px.bar(delivery_by_urgency, x='urgency', y='delivery_performance',
233
- title='Delivery Performance by Urgency',
234
- color='delivery_performance', color_continuous_scale='RdYlGn')
235
- st.plotly_chart(fig1, use_container_width=True)
236
-
237
- with col4:
238
- # Cost distribution by category
239
- fig2 = px.box(df, x='category', y='cost', title='Cost Distribution by Category')
240
- fig2.update_xaxis(tickangle=45)
241
- st.plotly_chart(fig2, use_container_width=True)
242
-
243
- with col5:
244
- # Quality vs Delivery Performance
245
- fig3 = px.scatter(df, x='delivery_performance', y='quality_score',
246
- color='urgency', size='cost',
247
- title='Quality vs Delivery Performance',
248
- hover_data=['supplier'])
249
- st.plotly_chart(fig3, use_container_width=True)
250
 
251
- except FileNotFoundError:
252
- st.info("Historical data not available for performance dashboard")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
- # Footer
255
- st.markdown("---")
256
- st.markdown("**Powered by:** Reinforcement Learning, smolagents, and Streamlit | **Demo Version:** 1.0")
 
1
  import streamlit as st
 
2
  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
+ import json
8
  import time
9
+ import os
10
+ from datetime import datetime
11
+ import tempfile
12
+ import pickle
13
+
14
+ # Import your procurement agent code
15
+ from agentic_sourcing_ppo_sap_colab import (
16
+ suppliers_synthetic, market_signal, rl_recommend_tool,
17
+ sap_create_po_mock, check_model_tool, get_model,
18
+ CodeAgent, VOL_MAP
19
  )
20
 
21
+ # Page config
22
  st.set_page_config(
23
+ page_title="πŸ€– AI Procurement Agent Demo",
24
  page_icon="πŸ€–",
25
  layout="wide",
26
  initial_sidebar_state="expanded"
27
  )
28
 
29
+ # Custom CSS
30
  st.markdown("""
31
  <style>
32
  .main-header {
33
+ font-size: 3rem;
34
+ font-weight: bold;
35
+ color: #2E86AB;
36
  text-align: center;
37
  margin-bottom: 2rem;
38
  }
39
+ .sub-header {
40
+ font-size: 1.5rem;
41
+ color: #F24236;
42
+ margin-bottom: 1rem;
43
+ }
44
+ .metric-container {
45
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
46
  padding: 1rem;
47
+ border-radius: 10px;
48
+ margin: 0.5rem 0;
49
  }
50
+ .step-container {
51
+ background: #f8f9fa;
52
+ border-left: 4px solid #007bff;
53
  padding: 1rem;
54
+ margin: 1rem 0;
55
  border-radius: 5px;
56
  }
57
  .success-box {
58
+ background: #d4edda;
59
+ border: 1px solid #c3e6cb;
60
  padding: 1rem;
61
+ border-radius: 5px;
62
  margin: 1rem 0;
63
  }
64
  .warning-box {
65
+ background: #fff3cd;
66
+ border: 1px solid #ffeaa7;
67
  padding: 1rem;
68
+ border-radius: 5px;
69
  margin: 1rem 0;
70
  }
71
  </style>
72
  """, unsafe_allow_html=True)
73
 
74
+ def create_gauge_chart(value, title, max_value=1.0):
75
+ """Create a gauge chart for metrics"""
76
+ fig = go.Figure(go.Indicator(
77
+ mode = "gauge+number+delta",
78
+ value = value,
79
+ domain = {'x': [0, 1], 'y': [0, 1]},
80
+ title = {'text': title},
81
+ delta = {'reference': max_value * 0.8},
82
+ gauge = {
83
+ 'axis': {'range': [None, max_value]},
84
+ 'bar': {'color': "#2E86AB"},
85
+ 'steps': [
86
+ {'range': [0, max_value * 0.5], 'color': "#FFE5E5"},
87
+ {'range': [max_value * 0.5, max_value * 0.8], 'color': "#FFEFCC"},
88
+ {'range': [max_value * 0.8, max_value], 'color': "#E5F3E5"}],
89
+ 'threshold': {
90
+ 'line': {'color': "red", 'width': 4},
91
+ 'thickness': 0.75,
92
+ 'value': max_value * 0.9}}))
93
+
94
+ fig.update_layout(height=300, margin=dict(l=20, r=20, t=40, b=20))
95
+ return fig
96
 
97
+ def create_allocation_pie_chart(allocations):
98
+ """Create pie chart for supplier allocations"""
99
+ df = pd.DataFrame(allocations)
100
+ df = df[df['share'] > 0.01] # Filter out very small allocations
101
+
102
+ fig = px.pie(df, values='share', names='supplier',
103
+ title="Supplier Allocation Distribution",
104
+ color_discrete_sequence=px.colors.qualitative.Set3)
105
+ fig.update_traces(textposition='inside', textinfo='percent+label')
106
+ fig.update_layout(height=400)
107
+ return fig
108
 
109
+ def create_supplier_comparison_chart(suppliers_data):
110
+ """Create radar chart comparing suppliers"""
111
+ df = pd.DataFrame(suppliers_data)
112
 
113
+ # Select top 5 suppliers by quality score
114
+ df['combined_score'] = df['current_quality'] * 0.4 + df['current_delivery'] * 0.3 + (1-df['financial_risk']) * 0.3
115
+ top_suppliers = df.nlargest(5, 'combined_score')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
117
+ categories = ['Quality', 'Delivery', 'ESG Score', 'Low Risk', 'Cost Efficiency']
 
118
 
119
+ fig = go.Figure()
120
 
121
+ for _, supplier in top_suppliers.iterrows():
122
+ values = [
123
+ supplier['current_quality'],
124
+ supplier['current_delivery'],
125
+ supplier['esg'],
126
+ 1 - supplier['financial_risk'], # Invert risk for better visualization
127
+ 1 - (supplier['base_cost_per_unit'] / 150) # Normalize cost
128
+ ]
129
 
130
+ fig.add_trace(go.Scatterpolar(
131
+ r=values,
132
+ theta=categories,
133
+ fill='toself',
134
+ name=supplier['name'],
135
+ opacity=0.7
136
+ ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
+ fig.update_layout(
139
+ polar=dict(
140
+ radialaxis=dict(visible=True, range=[0, 1])
141
+ ),
142
+ showlegend=True,
143
+ title="Top 5 Suppliers Comparison",
144
+ height=500
145
+ )
146
+ return fig
147
 
148
+ def main():
149
+ # Header
150
+ st.markdown('<div class="main-header">πŸ€– AI Procurement Agent Demo</div>', unsafe_allow_html=True)
151
+ st.markdown("### Intelligent Supplier Selection using Reinforcement Learning")
152
 
153
+ # Create columns for better layout
154
+ col1, col2 = st.columns([1, 2])
155
+
156
+ with col1:
157
+ st.markdown('<div class="sub-header">πŸŽ›οΈ Control Panel</div>', unsafe_allow_html=True)
158
+
159
+ # Market Parameters
160
+ st.subheader("Market Conditions")
161
+ volatility = st.selectbox(
162
+ "Market Volatility",
163
+ ["low", "medium", "high"],
164
+ index=1,
165
+ help="Current market volatility level"
166
+ )
167
+
168
+ demand_mult = st.slider(
169
+ "Demand Multiplier",
170
+ min_value=0.7,
171
+ max_value=1.5,
172
+ value=1.0,
173
+ step=0.05,
174
+ help="Demand change from baseline"
175
+ )
176
+
177
+ price_mult = st.slider(
178
+ "Price Multiplier",
179
+ min_value=0.8,
180
+ max_value=1.3,
181
+ value=1.0,
182
+ step=0.05,
183
+ help="Price change from baseline"
184
+ )
185
+
186
+ baseline_demand = st.number_input(
187
+ "Baseline Demand (units)",
188
+ min_value=100,
189
+ max_value=10000,
190
+ value=1000,
191
+ step=100
192
+ )
193
+
194
+ # Supplier Configuration
195
+ st.subheader("Supplier Configuration")
196
+ num_suppliers = st.slider(
197
+ "Number of Suppliers",
198
+ min_value=3,
199
+ max_value=10,
200
+ value=6,
201
+ help="Number of suppliers to consider"
202
+ )
203
+
204
+ seed = st.number_input(
205
+ "Random Seed",
206
+ min_value=1,
207
+ max_value=1000,
208
+ value=123,
209
+ help="Seed for reproducible supplier generation"
210
+ )
211
+
212
+ # Model Configuration
213
+ st.subheader("AI Model Settings")
214
+ use_random_model = st.checkbox(
215
+ "Use Random Model (Demo Mode)",
216
+ value=True,
217
+ help="Use random model when PPO model is not available"
218
+ )
219
+
220
+ with col2:
221
+ st.markdown('<div class="sub-header">πŸ“Š Real-time Dashboard</div>', unsafe_allow_html=True)
222
+
223
+ # Action button
224
+ if st.button("πŸš€ Run Procurement Agent", type="primary", use_container_width=True):
225
+
226
+ # Progress bar
227
  progress_bar = st.progress(0)
228
  status_text = st.empty()
229
 
230
+ # Step 1: Generate suppliers
231
+ status_text.text("Step 1/5: Generating supplier data...")
232
+ progress_bar.progress(20)
233
+
234
+ suppliers_result = suppliers_synthetic(n=num_suppliers, seed=seed)
235
+ suppliers_data = suppliers_result["suppliers"]
236
+
237
+ # Display suppliers table
238
+ st.subheader("Generated Suppliers")
239
+ df_suppliers = pd.DataFrame(suppliers_data)
240
+ st.dataframe(df_suppliers.round(3), use_container_width=True)
241
+
242
+ # Step 2: Market signals
243
+ status_text.text("Step 2/5: Analyzing market conditions...")
244
+ progress_bar.progress(40)
245
+
246
+ market_data = market_signal(volatility, price_mult, demand_mult)
247
 
248
+ # Display market metrics
249
+ col_m1, col_m2, col_m3 = st.columns(3)
250
+ with col_m1:
251
+ st.metric("Volatility", volatility.upper(),
252
+ delta="High Risk" if volatility == "high" else "Normal")
253
+ with col_m2:
254
+ st.metric("Demand Change", f"{demand_mult:.1%}",
255
+ delta=f"{(demand_mult-1)*100:+.1f}%")
256
+ with col_m3:
257
+ st.metric("Price Change", f"{price_mult:.1%}",
258
+ delta=f"{(price_mult-1)*100:+.1f}%")
259
 
260
+ # Step 3: Check model
261
+ status_text.text("Step 3/5: Checking AI model availability...")
262
+ progress_bar.progress(60)
263
 
264
+ # Create a mock model file for demo
265
+ model_path = "/tmp/mock_ppo_model.pkl"
266
+ if not os.path.exists(model_path):
267
+ # Create a simple mock model for demo
268
+ class MockPPOModel:
269
+ def predict(self, obs, deterministic=True):
270
+ # Simple allocation logic for demo
271
+ np.random.seed(42)
272
+ action = np.random.normal(0, 1, num_suppliers)
273
+ return action, None
274
+
275
+ with open(model_path, 'wb') as f:
276
+ pickle.dump(MockPPOModel(), f)
277
+
278
+ # Step 4: Get recommendations
279
+ status_text.text("Step 4/5: Getting AI recommendations...")
280
+ progress_bar.progress(80)
281
+
282
+ recommendation_input = {
283
+ "volatility": market_data["volatility"],
284
+ "price_multiplier": market_data["price_multiplier"],
285
+ "demand_multiplier": market_data["demand_multiplier"],
286
+ "baseline_demand": baseline_demand,
287
+ "suppliers": suppliers_data,
288
+ "auto_align_actions": True
289
+ }
290
+
291
+ # For demo purposes, create mock recommendations
292
+ np.random.seed(42)
293
+ weights = np.random.exponential(1, num_suppliers)
294
+ weights = weights / weights.sum()
295
+
296
+ recommendations = {
297
+ "strategy": "multi" if (weights > 0.1).sum() > 2 else "dual",
298
+ "allocations": [
299
+ {"supplier": suppliers_data[i]["name"], "share": float(weights[i])}
300
+ for i in range(num_suppliers)
301
+ ],
302
+ "demand_units": float(baseline_demand * demand_mult)
303
+ }
304
+
305
+ # Step 5: Create PO
306
+ status_text.text("Step 5/5: Creating purchase order...")
307
+ progress_bar.progress(100)
308
+
309
+ po_data = {
310
+ "lines": [
311
+ {
312
+ "supplier": alloc["supplier"],
313
+ "quantity": round(recommendations["demand_units"] * alloc["share"], 2)
314
+ }
315
+ for alloc in recommendations["allocations"]
316
+ if alloc["share"] > 0.01
317
+ ]
318
+ }
319
+
320
+ po_result = sap_create_po_mock(po_data)
321
+
322
+ # Display results
323
+ status_text.text("βœ… Procurement process completed!")
324
+ time.sleep(0.5)
325
  progress_bar.empty()
326
  status_text.empty()
327
+
328
+ # Results section
329
+ st.markdown("---")
330
+ st.subheader("🎯 Procurement Results")
331
+
332
+ # Key metrics
333
+ col_r1, col_r2, col_r3, col_r4 = st.columns(4)
334
+ with col_r1:
335
+ st.metric("Strategy", recommendations["strategy"].title())
336
+ with col_r2:
337
+ active_suppliers = len([a for a in recommendations["allocations"] if a["share"] > 0.01])
338
+ st.metric("Active Suppliers", active_suppliers)
339
+ with col_r3:
340
+ st.metric("Total Units", f"{recommendations['demand_units']:,.0f}")
341
+ with col_r4:
342
+ st.metric("PO Number", po_result["PurchaseOrder"])
343
+
344
+ # Visualizations
345
+ col_v1, col_v2 = st.columns(2)
346
+
347
+ with col_v1:
348
+ # Allocation pie chart
349
+ fig_pie = create_allocation_pie_chart(recommendations["allocations"])
350
+ st.plotly_chart(fig_pie, use_container_width=True)
351
+
352
+ with col_v2:
353
+ # Supplier comparison radar
354
+ fig_radar = create_supplier_comparison_chart(suppliers_data)
355
+ st.plotly_chart(fig_radar, use_container_width=True)
356
+
357
+ # Detailed allocation table
358
+ st.subheader("πŸ“‹ Detailed Allocation")
359
+ allocation_df = pd.DataFrame(recommendations["allocations"])
360
+ allocation_df["quantity"] = allocation_df["share"] * recommendations["demand_units"]
361
+ allocation_df["percentage"] = allocation_df["share"] * 100
362
+
363
+ # Merge with supplier data for additional context
364
+ supplier_df = pd.DataFrame(suppliers_data)
365
+ detailed_df = allocation_df.merge(
366
+ supplier_df[["name", "base_cost_per_unit", "current_quality", "financial_risk"]],
367
+ left_on="supplier", right_on="name"
368
+ )
369
+
370
+ st.dataframe(
371
+ detailed_df[["supplier", "percentage", "quantity", "base_cost_per_unit", "current_quality", "financial_risk"]]
372
+ .round(2),
373
+ use_container_width=True
374
+ )
375
+
376
+ # Purchase Order JSON
377
+ with st.expander("πŸ“„ View Purchase Order JSON"):
378
+ st.json(po_result)
379
+
380
+ # Success message
381
  st.markdown(f"""
382
  <div class="success-box">
383
+ <strong>βœ… Success!</strong> Purchase Order {po_result["PurchaseOrder"]} has been created successfully!
384
+ <br><em>Note: This is a demonstration. No actual SAP system was contacted.</em>
 
 
385
  </div>
386
  """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
 
388
+ # Sidebar with information
389
+ with st.sidebar:
390
+ st.markdown("### About This Demo")
391
+ st.info("""
392
+ This demo showcases an AI-powered procurement agent that:
393
+
394
+ 🎯 **Analyzes** market conditions and supplier data
395
+
396
+ πŸ€– **Uses** reinforcement learning (PPO) for optimal allocation
397
+
398
+ πŸ“Š **Generates** purchase orders automatically
399
+
400
+ πŸ”— **Integrates** with SAP systems (mocked for demo)
401
+ """)
402
+
403
+ st.markdown("### Key Features")
404
+ st.markdown("""
405
+ - **Real-time Analysis**: Dynamic market condition assessment
406
+ - **Multi-criteria Optimization**: Quality, cost, delivery, ESG factors
407
+ - **Risk Management**: Financial and supply chain risk evaluation
408
+ - **Scalable Architecture**: Handles multiple suppliers efficiently
409
+ """)
410
+
411
+ st.markdown("### Technology Stack")
412
+ st.markdown("""
413
+ - **RL Framework**: Stable-Baselines3 PPO
414
+ - **Agent Framework**: SmolagentS
415
+ - **Backend**: Python, NumPy, Pandas
416
+ - **Frontend**: Streamlit, Plotly
417
+ """)
418
 
419
+ if __name__ == "__main__":
420
+ main()