Noo88ear commited on
Commit
3001f99
Β·
verified Β·
1 Parent(s): b6568b8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +650 -50
app.py CHANGED
@@ -1,64 +1,664 @@
1
- import gradio as gr
2
- from huggingface_hub import InferenceClient
3
-
4
  """
5
- For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
 
 
6
  """
7
- client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
- def respond(
11
- message,
12
- history: list[tuple[str, str]],
13
- system_message,
14
- max_tokens,
15
- temperature,
16
- top_p,
17
- ):
18
- messages = [{"role": "system", "content": system_message}]
19
 
20
- for val in history:
21
- if val[0]:
22
- messages.append({"role": "user", "content": val[0]})
23
- if val[1]:
24
- messages.append({"role": "assistant", "content": val[1]})
25
 
26
- messages.append({"role": "user", "content": message})
 
 
27
 
28
- response = ""
29
 
30
- for message in client.chat_completion(
31
- messages,
32
- max_tokens=max_tokens,
33
- stream=True,
34
- temperature=temperature,
35
- top_p=top_p,
36
- ):
37
- token = message.choices[0].delta.content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
- response += token
40
- yield response
 
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
- """
44
- For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
45
- """
46
- demo = gr.ChatInterface(
47
- respond,
48
- additional_inputs=[
49
- gr.Textbox(value="You are a friendly Chatbot.", label="System message"),
50
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
51
- gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
52
- gr.Slider(
53
- minimum=0.1,
54
- maximum=1.0,
55
- value=0.95,
56
- step=0.05,
57
- label="Top-p (nucleus sampling)",
58
- ),
59
- ],
60
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
 
 
 
62
 
63
  if __name__ == "__main__":
64
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
+ MCPDB Agentic Exception Determination System
3
+ 6-Core Agent Financial Reconciliation with Excel Integration
4
+ Deployed on Hugging Face Spaces with Gradio Interface
5
  """
 
6
 
7
+ import gradio as gr
8
+ import os
9
+ import json
10
+ import logging
11
+ import asyncio
12
+ import pandas as pd
13
+ from datetime import datetime, timedelta
14
+ from typing import Dict, List, Optional, Any, Tuple
15
+ from PIL import Image
16
+ import io
17
+ import uuid
18
+ import threading
19
+ import time
20
+ from collections import defaultdict, deque
21
 
22
+ # Load environment variables
23
+ try:
24
+ from dotenv import load_dotenv
25
+ load_dotenv()
26
+ except ImportError:
27
+ pass
 
 
 
28
 
29
+ # Configure logging
30
+ logging.basicConfig(level=logging.INFO)
31
+ logger = logging.getLogger(__name__)
 
 
32
 
33
+ # ---------------------------------------------------------------------------
34
+ # CONSTANTS & CONFIGURATION
35
+ # ---------------------------------------------------------------------------
36
 
37
+ AGENT_TYPES = ["custodian", "cash", "trade", "corporate", "orchestrator", "web"]
38
 
39
+ EXCEL_TEMPLATES = {
40
+ "custodian": {
41
+ "Portfolio_Positions": ["Security_ID", "Security_Name", "Quantity", "Price", "Market_Value", "Last_Updated"],
42
+ "Holdings_History": ["Trade_ID", "Security_ID", "Quantity", "Trade_Date", "Settlement_Date", "Status"],
43
+ "Reconciliation_Check": ["Trade_ID", "Expected", "Actual", "Match", "Exception_Flag"]
44
+ },
45
+ "cash": {
46
+ "Cash_Flows": ["Trade_ID", "Amount", "Currency", "Type", "Date", "Status"],
47
+ "Account_Balances": ["Account", "Currency", "Available", "Pending", "Total"],
48
+ "Liquidity_Analysis": ["Date", "Available_Cash", "Pending_Traded", "Net_Cash", "Risk_Level"]
49
+ },
50
+ "trade": {
51
+ "Trade_Feed": ["Trade_ID", "Security_ID", "Side", "Quantity", "Price", "Broker", "Status"],
52
+ "Settlement_Details": ["Trade_ID", "Expected_Settle", "Actual_Settle", "Paired", "Exception"],
53
+ "Trade_Verification": ["Field", "Expected", "Actual", "Error", "Resolved"]
54
+ },
55
+ "corporate": {
56
+ "Action_Calendar": ["Security_ID", "Action_Type", "Ex_Date", "Record_Date", "Payment_Date", "Ratio"],
57
+ "Impact_Analysis": ["Security_ID", "Pre_Quantity", "Post_Quantity", "Impact_Amount"],
58
+ "Events_Log": ["Event_ID", "Security_ID", "Effective_Date", "Action_Type", "Processed"]
59
+ },
60
+ "orchestrator": {
61
+ "Agent_Status": ["Agent", "Status", "Last_Activity", "Processed_Count", "Error_Count"],
62
+ "Decision_Log": ["Decision_ID", "Trade_ID", "Agent_Votes", "Final_Decision", "Confidence"],
63
+ "Exception_Registry": ["Exception_ID", "Type", "Severity", "Status", "Assigned_Agent"]
64
+ },
65
+ "web": {
66
+ "Market_Data": ["Security_ID", "Price", "Volume", "As_Of", "Source"],
67
+ "News_Feeds": ["Security_ID", "Headline", "Impact", "Published", "Relevance"],
68
+ "Analyst_Reports": ["Security_ID", "Analyst", "Rating", "Price_Target", "Date"]
69
+ }
70
+ }
71
 
72
+ # ---------------------------------------------------------------------------
73
+ # IN-MEMORY STORAGE (for Hugging Face lightweight deployment)
74
+ # ---------------------------------------------------------------------------
75
 
76
+ class InMemoryStore:
77
+ def __init__(self):
78
+ self.trades = defaultdict(dict)
79
+ self.exceptions = defaultdict(dict)
80
+ self.agents = {agent_type: {
81
+ "status": "active",
82
+ "last_activity": datetime.now(),
83
+ "processed_count": 0,
84
+ "error_count": 0,
85
+ "workbook_data": {}
86
+ } for agent_type in AGENT_TYPES}
87
+ self.reconciliation_history = deque(maxlen=1000)
88
+ self.lock = threading.Lock()
89
+
90
+ def get_next_exception_id(self) -> str:
91
+ return f"EXC_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:8]}"
92
+
93
+ def get_next_trade_id(self) -> str:
94
+ return f"TRD_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:8]}"
95
 
96
+ store = InMemoryStore()
97
+
98
+ # ---------------------------------------------------------------------------
99
+ # AGENT IMPLEMENTATIONS
100
+ # ---------------------------------------------------------------------------
101
+
102
+ class BaseAgent:
103
+ def __init__(self, agent_type: str):
104
+ self.type = agent_type
105
+ self.logger = logging.getLogger(f"agent.{agent_type}")
106
+
107
+ def process_trade(self, trade_data: Dict[str, Any]) -> Dict[str, Any]:
108
+ """Process individual trade and return verdict"""
109
+ raise NotImplementedError
110
+
111
+ def get_status(self) -> Dict[str, Any]:
112
+ return store.agents[self.type]
113
+
114
+ def update_activity(self):
115
+ store.agents[self.type]["last_activity"] = datetime.now()
116
+ store.agents[self.type]["processed_count"] += 1
117
+
118
+ class CustodianAgent(BaseAgent):
119
+ def __init__(self):
120
+ super().__init__("custodian")
121
+
122
+ def process_trade(self, trade: Dict[str, Any]) -> Dict[str, Any]:
123
+ self.update_activity()
124
+
125
+ # Simulate security verification
126
+ quantity = trade.get("quantity", 0)
127
+ security = trade.get("security_id", "")
128
+
129
+ # Check if we have enough positions in our "ledger"
130
+ verdict = "PASS" if quantity > 0 else "FAIL: Insufficient positions"
131
+
132
+ return {
133
+ "verdict": verdict,
134
+ "details": f"Custodian verification: {quantity} {security} confirmed available",
135
+ "timestamp": datetime.now().isoformat()
136
+ }
137
+
138
+ class CashAgent(BaseAgent):
139
+ def __init__(self):
140
+ super().__init__("cash")
141
+
142
+ def process_trade(self, trade: Dict[str, Any]) -> Dict[str, Any]:
143
+ self.update_activity()
144
+
145
+ amount = trade.get("amount", float(trade.get("quantity", 0)) * float(trade.get("price", 0)))
146
+
147
+ # Simulate cash availability
148
+ verdict = "PASS" if amount > 0 else "FAIL: Insufficient funds"
149
+
150
+ return {
151
+ "verdict": verdict,
152
+ "details": f"Cash verification: ${amount:,.2f} available",
153
+ "timestamp": datetime.now().isoformat()
154
+ }
155
+
156
+ class TradeAgent(BaseAgent):
157
+ def __init__(self):
158
+ super().__init__("trade")
159
+
160
+ def process_trade(self, trade: Dict[str, Any]) -> Dict[str, Any]:
161
+ self.update_activity()
162
+
163
+ # Validate trade data structure
164
+ required_fields = ["trade_id", "security_id", "quantity", "price"]
165
+ missing_fields = [f for f in required_fields if not trade.get(f)]
166
+
167
+ verdict = "PASS" if not missing_fields else f"FAIL: Missing required fields: {', '.join(missing_fields)}"
168
+
169
+ return {
170
+ "verdict": verdict,
171
+ "details": "Trade file validation completed",
172
+ "missing_fields": missing_fields,
173
+ "timestamp": datetime.now().isoformat()
174
+ }
175
+
176
+ class CorporateAgent(BaseAgent):
177
+ def __init__(self):
178
+ super().__init__("corporate")
179
+
180
+ def process_trade(self, trade: Dict[str, Any]) -> Dict[str, Any]:
181
+ self.update_activity()
182
+
183
+ # Check for corporate actions affecting this security
184
+ security_id = trade.get("security_id", "")
185
+
186
+ # Simulate corporate action check
187
+ corporate_actions = ["stock_split", "dividend", "merger"]
188
+ impacting_actions = [ca for ca in corporate_actions if ca in str(security_id).lower()]
189
+
190
+ if impacting_actions:
191
+ verdict = f"REVIEW: Corporate action detected: {', '.join(impacting_actions)}"
192
+ else:
193
+ verdict = "PASS: No corporate actions impacting this security"
194
+
195
+ return {
196
+ "verdict": verdict,
197
+ "details": f"Corporate action analysis: {verdict}",
198
+ "timestamp": datetime.now().isoformat()
199
+ }
200
+
201
+ class OrchestratorAgent(BaseAgent):
202
+ def __init__(self):
203
+ super().__init__("orchestrator")
204
+ self.agents = {
205
+ "custodian": CustodianAgent(),
206
+ "cash": CashAgent(),
207
+ "trade": TradeAgent(),
208
+ "corporate": CorporateAgent()
209
+ }
210
+
211
+ def process_trade(self, trade: Dict[str, Any]) -> Dict[str, Any]:
212
+ self.update_activity()
213
+
214
+ trade_id = trade.get("trade_id", store.get_next_trade_id())
215
+
216
+ # Get all agent decisions
217
+ agent_decisions = {}
218
+ exception_flags = []
219
+
220
+ for agent_name, agent in self.agents.items():
221
+ decision = agent.process_trade(trade)
222
+ agent_decisions[agent_name] = decision
223
+
224
+ if "FAIL" in decision["verdict"] or "REVIEW" in decision["verdict"]:
225
+ exception_flags.append({
226
+ "agent": agent_name,
227
+ "issue": decision["verdict"],
228
+ "details": decision["details"]
229
+ })
230
+
231
+ # Determine final outcome
232
+ if exception_flags:
233
+ exception_id = store.get_next_exception_id()
234
+ exception = {
235
+ "id": exception_id,
236
+ "trade_id": trade_id,
237
+ "type": "discrepancy",
238
+ "severity": "high" if any("FAIL" in flag["issue"] for flag in exception_flags) else "medium",
239
+ "status": "open",
240
+ "agents": agent_decisions,
241
+ "created_at": datetime.now().isoformat()
242
+ }
243
+
244
+ store.exceptions[exception_id] = exception
245
+ store.reconciliation_history.append({
246
+ "trade_id": trade_id,
247
+ "status": "exception",
248
+ "exception_id": exception_id
249
+ })
250
+
251
+ result = {
252
+ "status": "exception",
253
+ "exception_id": exception_id,
254
+ "message": f"Reconciliation exception detected: {len(exception_flags)} issues found",
255
+ "agents": agent_decisions
256
+ }
257
+ else:
258
+ result = {
259
+ "status": "success",
260
+ "message": "Trade successfully reconciled - no exceptions",
261
+ "agents": agent_decisions
262
+ }
263
+
264
+ store.trades[trade_id] = {
265
+ "data": trade,
266
+ "result": result,
267
+ "timestamp": datetime.now().isoformat()
268
+ }
269
+
270
+ return result
271
+
272
+ def resolve_exception(self, exception_id: str, resolution: Dict[str, Any]) -> Dict[str, Any]:
273
+ if exception_id not in store.exceptions:
274
+ return {"error": "Exception not found"}
275
+
276
+ exception = store.exceptions[exception_id]
277
+ exception["status"] = "resolved"
278
+ exception["resolution"] = resolution
279
+ exception["resolved_at"] = datetime.now().isoformat()
280
+
281
+ store.reconciliation_history.append({
282
+ "exception_id": exception_id,
283
+ "status": "resolved",
284
+ "resolution": resolution
285
+ })
286
+
287
+ return {"success": True, "exception": exception}
288
+
289
+ class WebAgent(BaseAgent):
290
+ def __init__(self):
291
+ super().__init__("web")
292
+
293
+ def fetch_market_data(self, securities: List[str]) -> Dict[str, Any]:
294
+ self.update_activity()
295
+
296
+ # Simulate real-time market data
297
+ market_data = {}
298
+ for security in securities:
299
+ market_data[security] = {
300
+ "current_price": 100.0 + (hash(security) % 50),
301
+ "last_updated": datetime.now().isoformat(),
302
+ "source": "simulated_web_data"
303
+ }
304
+
305
+ return {
306
+ "data": market_data,
307
+ "source": "web_agent",
308
+ "timestamp": datetime.now().isoformat()
309
+ }
310
+
311
+ # Initialize agents
312
+ orchestrator = OrchestratorAgent()
313
+ web_agent = WebAgent()
314
+ all_agents = {
315
+ "custodian": CustodianAgent(),
316
+ "cash": CashAgent(),
317
+ "trade": TradeAgent(),
318
+ "corporate": CorporateAgent(),
319
+ "orchestrator": orchestrator,
320
+ "web": web_agent
321
+ }
322
+
323
+ # ---------------------------------------------------------------------------
324
+ # EXCEL WORKBOOK MANAGEMENT
325
+ # ---------------------------------------------------------------------------
326
+
327
+ class ExcelManager:
328
+ def __init__(self):
329
+ self.workbooks_dir = "excel_workbooks"
330
+ os.makedirs(self.workbooks_dir, exist_ok=True)
331
+ self.init_workbooks()
332
+
333
+ def init_workbooks(self):
334
+ """Initialize Excel workbooks for all agents"""
335
+ for agent_type, sheets in EXCEL_TEMPLATES.items():
336
+ file_path = f"{self.workbooks_dir}/{agent_type}_agent_workbook.xlsx"
337
+ if not os.path.exists(file_path):
338
+ # Create mock Excel file (for demo purposes)
339
+ try:
340
+ import pandas as pd
341
+ with pd.ExcelWriter(file_path, engine='openpyxl') as writer:
342
+ for sheet_name, columns in sheets.items():
343
+ df = pd.DataFrame(columns=columns)
344
+ df.to_excel(writer, sheet_name=sheet_name, index=False)
345
+ except ImportError:
346
+ # Fallback: create info file
347
+ with open(file_path.replace('.xlsx', '.txt'), 'w') as f:
348
+ f.write(f"Excel workbook for {agent_type} agent\n")
349
+ for sheet_name, columns in sheets.items():
350
+ f.write(f"Sheet: {sheet_name}\n")
351
+ f.write(f"Columns: {', '.join(columns)}\n")
352
+
353
+ def get_agent_workbook(self, agent_type: str) -> str:
354
+ return f"{self.workbooks_dir}/{agent_type}_agent_workbook.xlsx"
355
+
356
+ def list_all_workbooks(self) -> List[Dict[str, Any]]:
357
+ workbooks = []
358
+ for agent_type in AGENT_TYPES:
359
+ path = self.get_agent_workbook(agent_type)
360
+ if os.path.exists(path):
361
+ stat = os.stat(path)
362
+ workbooks.append({
363
+ "agent_type": agent_type,
364
+ "path": path,
365
+ "last_modified": datetime.fromtimestamp(stat.st_mtime).isoformat()
366
+ })
367
+ return workbooks
368
+
369
+ excel_manager = ExcelManager()
370
+
371
+ # ---------------------------------------------------------------------------
372
+ # GRADIO UI & API FUNCTIONS
373
+ # ---------------------------------------------------------------------------
374
+
375
+ def submit_trade(
376
+ security_id: str,
377
+ quantity: float,
378
+ price: float,
379
+ trade_side: str,
380
+ broker: str,
381
+ counterparty: str,
382
+ settlement_date: str
383
+ ) -> str:
384
+ """Submit trade for reconciliation processing"""
385
+
386
+ if not security_id or quantity <= 0 or price <= 0:
387
+ return json.dumps({"error": "Invalid trade parameters"})
388
+
389
+ trade_data = {
390
+ "trade_id": store.get_next_trade_id(),
391
+ "security_id": security_id,
392
+ "quantity": quantity,
393
+ "price": price,
394
+ "trade_side": trade_side,
395
+ "broker": broker,
396
+ "counterparty": counterparty,
397
+ "settlement_date": settlement_date,
398
+ "amount": quantity * price,
399
+ "submitted_at": datetime.now().isoformat()
400
+ }
401
+
402
+ # Process through orchestrator
403
+ result = orchestrator.process_trade(trade_data)
404
+
405
+ return json.dumps(result, indent=2, default=str)
406
+
407
+ def get_agent_status(agent_type: str) -> str:
408
+ """Get status of specific agent"""
409
+ if agent_type not in all_agents:
410
+ return json.dumps({"error": f"Unknown agent: {agent_type}"})
411
+
412
+ agent = all_agents[agent_type]
413
+ return json.dumps(agent.get_status(), indent=2, default=str)
414
+
415
+ def list_exceptions() -> str:
416
+ """List all current exceptions"""
417
+ exceptions = list(store.exceptions.values())
418
+ return json.dumps(exceptions, indent=2, default=str)
419
+
420
+ def resolve_exception_manually(
421
+ exception_id: str,
422
+ resolution_action: str,
423
+ justification: str
424
+ ) -> str:
425
+ """Resolve a specific exception"""
426
+ resolution = {
427
+ "action": resolution_action,
428
+ "justification": justification,
429
+ "resolved_by": "manual"
430
+ }
431
+
432
+ result = orchestrator.resolve_exception(exception_id, resolution)
433
+ return json.dumps(result, indent=2, default=str)
434
+
435
+ def get_dashboard() -> str:
436
+ """Get system dashboard data"""
437
+ dashboard = {
438
+ "summary": {
439
+ "total_trades": len(store.trades),
440
+ "open_exceptions": len([e for e in store.exceptions.values() if e["status"] == "open"]),
441
+ "resolved_exceptions": len([e for e in store.exceptions.values() if e["status"] == "resolved"]),
442
+ "last_activity": max(trade["timestamp"] for trade in store.trades.values()) if store.trades else None
443
+ },
444
+ "agents": {
445
+ agent_type: agent.get_status()
446
+ for agent_type, agent in all_agents.items()
447
+ },
448
+ "recent_exceptions": list(store.reconciliation_history)[-10:]
449
+ }
450
+ return json.dumps(dashboard, indent=2, default=str)
451
+
452
+ # Demo scenario functions
453
+ def demo_normal_settlement() -> str:
454
+ """Demo: Normal perfect reconciliation"""
455
+ trade = {
456
+ "security_id": "AAPL",
457
+ "quantity": 100,
458
+ "price": 150.0,
459
+ "trade_side": "BUY",
460
+ "broker": "GOLD",
461
+ "counterparty": "MORG",
462
+ "settlement_date": datetime.now().strftime("%Y-%m-%d"),
463
+ "amount": 15000.0
464
+ }
465
+
466
+ trade["trade_id"] = store.get_next_trade_id()
467
+ return json.dumps(orchestrator.process_trade(trade), indent=2, default=str)
468
+
469
+ def demo_failed_trade() -> str:
470
+ """Demo: Failed trade due to insufficient funds"""
471
+ trade = {
472
+ "security_id": "TSLA",
473
+ "quantity": 50000,
474
+ "price": 1000.0, # Too large amount
475
+ "trade_side": "BUY",
476
+ "broker": "BROK",
477
+ "counterparty": "COLL",
478
+ "settlement_date": datetime.now().strftime("%Y-%m-%d"),
479
+ "amount": 50000000.0
480
+ }
481
+
482
+ trade["trade_id"] = store.get_next_trade_id()
483
+ return json.dumps(orchestrator.process_trade(trade), indent=2, default=str)
484
+
485
+ def demo_corporate_action() -> str:
486
+ """Demo: Apple stock split scenario"""
487
+ trade = {
488
+ "security_id": "AAPL_SPLIT",
489
+ "quantity": 1000,
490
+ "price": 175.0,
491
+ "trade_side": "SELL",
492
+ "broker": "SPLLIT",
493
+ "counterparty": "BUYER",
494
+ "settlement_date": datetime.now().strftime("%Y-%m-%d"),
495
+ "amount": 175000.0
496
+ }
497
+
498
+ trade["trade_id"] = store.get_next_trade_id()
499
+ return json.dumps(orchestrator.process_trade(trade), indent=2, default=str)
500
+
501
+ def get_excel_workbook(agent_type: str) -> str:
502
+ """Get Excel workbook for agent"""
503
+ return json.dumps({
504
+ "workbook_path": excel_manager.get_agent_workbook(agent_type),
505
+ "exists": os.path.exists(excel_manager.get_agent_workbook(agent_type))
506
+ })
507
+
508
+ # ---------------------------------------------------------------------------
509
+ # GRADIO INTERFACE
510
+ # ---------------------------------------------------------------------------
511
+
512
+ def create_gradio_interface() -> gr.Blocks:
513
+ """Create the Gradio interface"""
514
+
515
+ with gr.Blocks(title="MCPDB Agentic Exception Determination", theme=gr.themes.Soft()) as app:
516
+ gr.Markdown("""
517
+ # 🏦 MCPDB Agentic Exception Determination System
518
+ ### 6-Core Agent Financial Reconciliation with Excel Integration
519
+
520
+ **Deployed on Hugging Face Spaces with built-in Excel MCP Server support**
521
+ """)
522
+
523
+ # Main interface
524
+ with gr.Tabs():
525
+
526
+ # Reconciliation Tab
527
+ with gr.Tab("πŸ” Trade Reconciliation"):
528
+ with gr.Row():
529
+ with gr.Column():
530
+ gr.Markdown("### Submit New Trade")
531
+
532
+ security_id = gr.Textbox(label="Security ID", value="AAPL")
533
+ quantity = gr.Number(label="Quantity", value=100)
534
+ price = gr.Number(label="Price", value=150.0)
535
+ trade_side = gr.Dropdown(
536
+ choices=["BUY", "SELL"],
537
+ value="BUY",
538
+ label="Trade Side"
539
+ )
540
+ broker = gr.Textbox(label="Broker", value="GOLD")
541
+ counterparty = gr.Textbox(label="Counterparty", value="MORG")
542
+ settlement_date = gr.Textbox(
543
+ label="Settlement Date",
544
+ value=datetime.now().strftime("%Y-%m-%d")
545
+ )
546
+
547
+ submit_btn = gr.Button("πŸ”„ Submit for Reconciliation", variant="primary")
548
+
549
+ gr.Markdown("### Demo Scenarios")
550
+ demo_normal_btn = gr.Button("🎯 Normal Settlement", size="sm")
551
+ demo_failed_btn = gr.Button("πŸ’₯ Failed Trade", size="sm")
552
+ demo_corp_btn = gr.Button("🍎 Corporate Action", size="sm")
553
+
554
+ result_output = gr.JSON(label="Reconciliation Result")
555
+
556
+ # Exceptions Tab
557
+ with gr.Tab("⚠️ Exceptions"):
558
+
559
+ with gr.Row():
560
+ with gr.Column():
561
+ gr.Markdown("#### Current Exceptions")
562
+ exceptions_output = gr.JSON(label="Open Exceptions")
563
+ refresh_exceptions_btn = gr.Button("πŸ”„ Refresh Exceptions")
564
+
565
+ with gr.Column():
566
+ gr.Markdown("#### Resolve Exception")
567
+ exception_id_input = gr.Textbox(label="Exception ID")
568
+ resolution_action = gr.Dropdown(
569
+ choices=["APPROVE", "REJECT", "REVIEW", "ESCALATE"],
570
+ value="APPROVE",
571
+ label="Resolution Action"
572
+ )
573
+ justification = gr.Textbox(
574
+ label="Justification",
575
+ value="Manual review and approval provided"
576
+ )
577
+ resolve_btn = gr.Button("βœ… Resolve Exception")
578
+
579
+ resolution_output = gr.JSON(label="Resolution Result")
580
+
581
+ # Agents Tab
582
+ with gr.Tab("πŸ€– Agents"):
583
+ gr.Markdown("### Agent Status Dashboard")
584
+
585
+ agent_monitor = gr.JSON(label="Agent Status")
586
+ refresh_agents_btn = gr.Button("πŸ”„ Refresh Agent Status")
587
+
588
+ with gr.Row():
589
+ for agent in AGENT_TYPES:
590
+ btn = gr.Button(f"Status: {agent.title()}")
591
+ btn.click(fn=lambda a=agent: get_agent_status(a), outputs=agent_monitor)
592
+
593
+ # Excel Tab
594
+ with gr.Tab("πŸ“Š Excel Integration"):
595
+ gr.Markdown("### Agent Excel Workbooks")
596
+
597
+ workbooks_list = gr.JSON(label="Agent Workbooks")
598
+ refresh_workbooks_btn = gr.Button("πŸ”„ Refresh Workbooks")
599
+
600
+ with gr.Row():
601
+ for agent_type in AGENT_TYPES:
602
+ btn = gr.Button(f"πŸ“‹ {agent_type.title()}")
603
+ btn.click(lambda a=agent_type: get_excel_workbook(a), outputs=None)
604
+ btn.style = "secondary"
605
+
606
+ # Dashboard Tab
607
+ with gr.Tab("πŸ“ˆ Dashboard"):
608
+ gr.Markdown("### System Dashboard")
609
+ dashboard_output = gr.JSON(label="Dashboard Data")
610
+ refresh_dashboard_btn = gr.Button("πŸ”„ Refresh Dashboard")
611
+
612
+ gr.Markdown("---")
613
+ gr.Markdown(f"""
614
+ <div style='text-align: center; color: #666; font-size: 0.9rem; padding: 20px;'>
615
+ <p>🏦 MCPDB Agentic Exception Determination System v1.0</p>
616
+ <p>6-Core Agents: Custodian | Cash | Trade | Corporate | Orchestrator | Web</p>
617
+ <p>Built for Hugging Face Spaces with Excel MCP Server Integration</p>
618
+ <p>Agent Status: {' | '.join([f'{a.title()}: {store.agents[a]["status"]}' for a in AGENT_TYPES])}</p>
619
+ </div>
620
+ """)
621
+
622
+ # Event handlers
623
+ submit_btn.click(
624
+ fn=submit_trade,
625
+ inputs=[security_id, quantity, price, trade_side, broker, counterparty, settlement_date],
626
+ outputs=[result_output]
627
+ )
628
+
629
+ # Demo scenarios
630
+ demo_normal_btn.click(fn=demo_normal_settlement, outputs=[result_output])
631
+ demo_failed_btn.click(fn=demo_failed_trade, outputs=[result_output])
632
+ demo_corp_btn.click(fn=demo_corporate_action, outputs=[result_output])
633
+
634
+ refresh_exceptions_btn.click(fn=list_exceptions, outputs=[exceptions_output])
635
+ refresh_agents_btn.click(fn=get_dashboard, outputs=[agent_monitor])
636
+ refresh_dashboard_btn.click(fn=get_dashboard, outputs=[dashboard_output])
637
+ refresh_workbooks_btn.click(fn=excel_manager.list_all_workbooks, outputs=[workbooks_list])
638
+ resolve_btn.click(
639
+ fn=resolve_exception_manually,
640
+ inputs=[exception_id_input, resolution_action, justification],
641
+ outputs=[resolution_output]
642
+ )
643
+
644
+ # Initial loads
645
+ app.load(fn=list_exceptions, outputs=[exceptions_output])
646
+ app.load(fn=get_dashboard, outputs=[dashboard_output])
647
+ app.load(fn=excel_manager.list_all_workbooks, outputs=[workbooks_list])
648
+
649
+ return app
650
 
651
+ # ---------------------------------------------------------------------------
652
+ # LAUNCH
653
+ # ---------------------------------------------------------------------------
654
 
655
  if __name__ == "__main__":
656
+ app = create_gradio_interface()
657
+
658
+ # Initialize with some demo data
659
+ logger.info("πŸš€ Starting MCPDB Agentic Exception Determination System")
660
+ logger.info(f"πŸ”— Agents Initialized: {', '.join(AGENT_TYPES)}")
661
+ logger.info("πŸ“Š Excel workbooks created for all agents")
662
+ logger.info("🎯 Demo scenarios ready for testing")
663
+
664
+ app.launch(server_name="0.0.0.0", server_port=8080, share=True)