PD03 commited on
Commit
bc95660
·
verified ·
1 Parent(s): 26fc8f8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +3 -344
app.py CHANGED
@@ -1,347 +1,6 @@
1
  import gradio as gr
2
- import requests
3
- import json
4
- import re
5
- import time
6
- from datetime import datetime, timedelta
7
- import logging
8
- from typing import Dict, Any, Optional, List
9
- import os
10
 
11
- # Configure logging
12
- logging.basicConfig(level=logging.INFO)
13
- logger = logging.getLogger(__name__)
14
 
15
- # Configuration
16
- MCP_URL = "https://long-planets-wish.loca.lt/mcp"
17
- API_URL = "https://api-inference.huggingface.co/models/microsoft/DialoGPT-medium"
18
-
19
- # Fallback API URLs in case primary fails
20
- FALLBACK_APIS = [
21
- "https://api-inference.huggingface.co/models/microsoft/DialoGPT-small",
22
- "https://api-inference.huggingface.co/models/gpt2"
23
- ]
24
-
25
- class MCPSAPClient:
26
- def __init__(self):
27
- self.session = requests.Session()
28
- self.session.timeout = 30
29
- self.max_retries = 3
30
- self.retry_delay = 2
31
-
32
- # Uncomment below if your API needs HF token and set it as a Space secret called "HF_TOKEN"
33
- # API_TOKEN = os.getenv("HF_TOKEN", None)
34
- # if API_TOKEN:
35
- # self.session.headers.update({"Authorization": f"Bearer {API_TOKEN}"})
36
-
37
- def query_llm_with_fallback(self, prompt: str, max_length: int = 150) -> str:
38
- apis_to_try = [API_URL] + FALLBACK_APIS
39
-
40
- for api_url in apis_to_try:
41
- for attempt in range(self.max_retries):
42
- try:
43
- logger.info(f"Attempting LLM query with {api_url}, attempt {attempt + 1}")
44
-
45
- # Different payload formats for different APIs
46
- if "DialoGPT" in api_url:
47
- payload = {
48
- "inputs": prompt,
49
- "parameters": {
50
- "max_new_tokens": max_length,
51
- "temperature": 0.3,
52
- "return_full_text": False
53
- }
54
- }
55
- else:
56
- payload = {
57
- "inputs": prompt,
58
- "parameters": {
59
- "max_length": max_length,
60
- "temperature": 0.3
61
- }
62
- }
63
-
64
- response = self.session.post(
65
- api_url,
66
- json=payload,
67
- timeout=30
68
- )
69
-
70
- if response.status_code == 200:
71
- result = response.json()
72
- # Handle different response formats
73
- if isinstance(result, list) and len(result) > 0:
74
- if "generated_text" in result[0]:
75
- return result[0]["generated_text"]
76
- elif "text" in result[0]:
77
- return result[0]["text"]
78
- elif isinstance(result, dict):
79
- if "generated_text" in result:
80
- return result["generated_text"]
81
- elif "text" in result:
82
- return result["text"]
83
- return str(result)
84
- elif response.status_code == 503:
85
- logger.warning(f"Model loading, waiting {self.retry_delay} seconds...")
86
- time.sleep(self.retry_delay)
87
- continue
88
- else:
89
- logger.warning(f"API returned status {response.status_code}")
90
- time.sleep(self.retry_delay)
91
- continue
92
-
93
- except requests.exceptions.RequestException as e:
94
- logger.error(f"Request failed: {e}")
95
- if attempt < self.max_retries - 1:
96
- time.sleep(self.retry_delay)
97
- continue
98
-
99
- except Exception as e:
100
- logger.error(f"Unexpected error: {e}")
101
- if attempt < self.max_retries - 1:
102
- time.sleep(self.retry_delay)
103
- continue
104
-
105
- logger.warning(f"All attempts failed for {api_url}, trying next API...")
106
-
107
- # If all APIs fail, return a fallback response
108
- return "Unable to process with LLM, using fallback parsing"
109
-
110
- def extract_query_params_robust(self, query: str) -> Dict[str, Any]:
111
- try:
112
- prompt = f"""
113
- Extract date and quantity from this query: '{query}'
114
- Return only JSON format:
115
- {{"date_from":"YYYY-MM-DD","min_quantity":number}}
116
-
117
- Examples:
118
- - "orders after 2023-04-01 with at least 10 units" -> {{"date_from":"2023-04-01","min_quantity":10}}
119
- - "show me orders from last month" -> {{"date_from":"2023-11-01","min_quantity":0}}
120
- """
121
- llm_response = self.query_llm_with_fallback(prompt)
122
- logger.info(f"LLM Response: {llm_response}")
123
-
124
- json_match = re.search(r'\{[^}]*\}', llm_response, re.DOTALL)
125
- if json_match:
126
- json_text = json_match.group()
127
- parsed = json.loads(json_text)
128
- if "date_from" in parsed and "min_quantity" in parsed:
129
- return parsed
130
-
131
- except Exception as e:
132
- logger.warning(f"LLM extraction failed: {e}")
133
-
134
- return self.extract_params_rule_based(query)
135
-
136
- def extract_params_rule_based(self, query: str) -> Dict[str, Any]:
137
- params = {"date_from": "2023-01-01", "min_quantity": 0}
138
- date_patterns = [
139
- r'(\d{4}-\d{2}-\d{2})', # YYYY-MM-DD
140
- r'(\d{2}/\d{2}/\d{4})', # MM/DD/YYYY
141
- r'(\d{1,2}/\d{1,2}/\d{4})', # M/D/YYYY
142
- ]
143
- for pattern in date_patterns:
144
- match = re.search(pattern, query)
145
- if match:
146
- date_str = match.group(1)
147
- try:
148
- if '-' in date_str:
149
- params["date_from"] = date_str
150
- elif '/' in date_str:
151
- parts = date_str.split('/')
152
- if len(parts) == 3:
153
- params["date_from"] = f"{parts[2]}-{parts[0].zfill(2)}-{parts[1].zfill(2)}"
154
- break
155
- except:
156
- continue
157
-
158
- quantity_patterns = [
159
- r'at least (\d+)',
160
- r'minimum (\d+)',
161
- r'more than (\d+)',
162
- r'> ?(\d+)',
163
- r'greater than (\d+)'
164
- ]
165
- for pattern in quantity_patterns:
166
- match = re.search(pattern, query, re.IGNORECASE)
167
- if match:
168
- try:
169
- params["min_quantity"] = int(match.group(1))
170
- break
171
- except:
172
- continue
173
-
174
- if "last month" in query.lower():
175
- last_month = datetime.now() - timedelta(days=30)
176
- params["date_from"] = last_month.strftime("%Y-%m-%d")
177
- elif "last week" in query.lower():
178
- last_week = datetime.now() - timedelta(days=7)
179
- params["date_from"] = last_week.strftime("%Y-%m-%d")
180
- elif "yesterday" in query.lower():
181
- yesterday = datetime.now() - timedelta(days=1)
182
- params["date_from"] = yesterday.strftime("%Y-%m-%d")
183
- elif "today" in query.lower():
184
- params["date_from"] = datetime.now().strftime("%Y-%m-%d")
185
-
186
- return params
187
-
188
- def query_mcp_server(self, params: Dict[str, Any]) -> Dict[str, Any]:
189
- for attempt in range(self.max_retries):
190
- try:
191
- payload = {
192
- "jsonrpc": "2.0",
193
- "method": "SAP.getFilteredProcOrder",
194
- "params": params,
195
- "id": f"dynamic-{int(time.time())}"
196
- }
197
-
198
- logger.info(f"MCP Request (attempt {attempt + 1}): {json.dumps(payload, indent=2)}")
199
-
200
- response = self.session.post(
201
- MCP_URL,
202
- json=payload,
203
- timeout=30
204
- )
205
-
206
- if response.status_code == 200:
207
- result = response.json()
208
- logger.info(f"MCP Response: {json.dumps(result, indent=2)}")
209
- return result
210
- else:
211
- logger.warning(f"MCP server returned status {response.status_code}: {response.text}")
212
- if attempt < self.max_retries - 1:
213
- time.sleep(self.retry_delay)
214
- continue
215
- else:
216
- return {"error": f"MCP server error: {response.status_code}"}
217
-
218
- except requests.exceptions.RequestException as e:
219
- logger.error(f"MCP request failed (attempt {attempt + 1}): {e}")
220
- if attempt < self.max_retries - 1:
221
- time.sleep(self.retry_delay)
222
- continue
223
- else:
224
- return {"error": f"Connection failed: {str(e)}"}
225
-
226
- return {"error": "Max retries exceeded"}
227
-
228
- def format_sap_results(self, results: List[Dict[str, Any]]) -> str:
229
- if not results:
230
- return "✅ Query executed successfully, but no matching orders found.\n\nTry adjusting your criteria:\n- Use a different date range\n- Lower the quantity threshold\n- Check if the SAP system has data for the specified period"
231
-
232
- formatted_results = []
233
- formatted_results.append(f"📊 **Found {len(results)} matching orders:**\n")
234
-
235
- for i, result in enumerate(results, 1):
236
- order_id = result.get('ProcessOrderConfirmation', 'N/A')
237
- material = result.get('Material', 'N/A')
238
- quantity = result.get('ConfirmedYieldQuantity', 'N/A')
239
- unit = result.get('ConfirmedYieldQuantityUnit', '')
240
- posting_date = result.get('PostingDate', 'N/A')
241
-
242
- formatted_results.append(
243
- f"**Order {i}:**\n"
244
- f"• Order ID: {order_id}\n"
245
- f"• Material: {material}\n"
246
- f"• Quantity: {quantity} {unit}\n"
247
- f"• Posting Date: {posting_date}\n"
248
- )
249
-
250
- return "\n".join(formatted_results)
251
-
252
- def agent_query(self, user_query: str) -> str:
253
- if not user_query.strip():
254
- return "❌ Please enter a query about SAP orders."
255
-
256
- try:
257
- logger.info(f"Processing query: {user_query}")
258
- params = self.extract_query_params_robust(user_query)
259
- logger.info(f"Extracted parameters: {params}")
260
-
261
- response = self.query_mcp_server(params)
262
-
263
- if "error" in response:
264
- return f"❌ **Error:** {response['error']}\n\n**Troubleshooting:**\n- Check if your MCP server is running\n- Verify the server URL is correct\n- Ensure the SAP system is accessible"
265
-
266
- results = response.get("result", [])
267
-
268
- formatted_output = self.format_sap_results(results)
269
-
270
- query_info = f"\n\n📝 **Query Details:**\n• Date from: {params.get('date_from', 'N/A')}\n• Minimum quantity: {params.get('min_quantity', 0)}"
271
-
272
- return formatted_output + query_info
273
-
274
- except Exception as e:
275
- logger.error(f"Unexpected error in agent_query: {e}")
276
- return f"❌ **Unexpected Error:** {str(e)}\n\nPlease try again or contact support if the issue persists."
277
-
278
- # Initialize the client
279
- client = MCPSAPClient()
280
-
281
- # Example queries for users
282
- example_queries = [
283
- "Show me orders after 2023-04-01 with at least 10 units",
284
- "Orders from last month with minimum 5 quantity",
285
- "Find orders from 2023-06-15 with at least 20 units",
286
- "Show me all orders from last week",
287
- "Orders with quantity greater than 50 from 2023-05-01"
288
- ]
289
-
290
- # Create Gradio interface
291
- def create_interface():
292
- with gr.Blocks(title="SAP MCP Agent", theme=gr.themes.Soft()) as iface:
293
- gr.Markdown("""
294
- # 🔍 SAP MCP Agent
295
-
296
- Query your SAP system naturally using the Model Context Protocol (MCP).
297
-
298
- **How to use:**
299
- - Ask about orders using natural language
300
- - Specify dates and quantities in your query
301
- - The system will extract parameters and query SAP for you
302
-
303
- **Example queries:**
304
- """)
305
-
306
- with gr.Row():
307
- with gr.Column():
308
- query_input = gr.Textbox(
309
- label="Enter your SAP query",
310
- placeholder="e.g., 'Show me orders after 2023-04-01 with at least 10 units'",
311
- lines=2
312
- )
313
-
314
- submit_btn = gr.Button("🔍 Query SAP", variant="primary")
315
-
316
- gr.Markdown("**Example queries:**")
317
- example_buttons = []
318
- for example in example_queries:
319
- btn = gr.Button(f"📋 {example}", size="sm")
320
- example_buttons.append(btn)
321
-
322
- with gr.Column():
323
- output = gr.Textbox(
324
- label="Results",
325
- lines=15,
326
- max_lines=20,
327
- show_copy_button=True
328
- )
329
-
330
- # Event handlers
331
- submit_btn.click(
332
- client.agent_query,
333
- inputs=[query_input],
334
- outputs=[output]
335
- )
336
-
337
- # Example button handlers
338
- for btn, example in zip(example_buttons, example_queries):
339
- btn.click(
340
- lambda x=example: x,
341
- outputs=[query_input]
342
- )
343
-
344
- return iface
345
-
346
- # Expose interface globally for Hugging Face Spaces (do not launch explicitly)
347
- iface = create_interface()
 
1
  import gradio as gr
 
 
 
 
 
 
 
 
2
 
3
+ def hello(name):
4
+ return f"Hello, {name}!"
 
5
 
6
+ iface = gr.Interface(fn=hello, inputs="text", outputs="text")