mgokg commited on
Commit
05ae9d4
·
verified ·
1 Parent(s): 07492c4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +203 -75
app.py CHANGED
@@ -2,51 +2,104 @@ import gradio as gr
2
  import os
3
  import json
4
  import asyncio
5
- from typing import Optional
6
- from mcp import ClientSession, StdioServerParameters
7
- from mcp.client.stdio import stdio_client
8
  import httpx
 
9
 
10
- class MCPTrainClient:
11
- """MCP Client für Zugverbindungsabfragen"""
12
 
13
  def __init__(self, server_url: str):
14
- self.server_url = server_url
15
- self.session: Optional[ClientSession] = None
16
  self.tools = []
 
17
 
18
- async def connect(self):
19
- """Stelle Verbindung zum MCP-Server her"""
20
  try:
21
- # Für Streamable HTTP Transport verwenden wir httpx
22
- # Die Session wird über die MCP-URL initialisiert
23
- self.session = ClientSession()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
- # Tools vom Server abrufen
26
- await self.list_tools()
27
- return True
 
 
 
 
 
28
  except Exception as e:
29
- print(f"Verbindungsfehler: {e}")
30
  return False
31
 
32
  async def list_tools(self):
33
  """Rufe verfügbare Tools vom MCP-Server ab"""
34
  try:
35
- if self.session:
36
- response = await self.session.list_tools()
37
- self.tools = response.tools
38
- print(f"Verfügbare Tools: {[tool.name for tool in self.tools]}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  except Exception as e:
40
  print(f"Fehler beim Abrufen der Tools: {e}")
41
 
42
- async def call_tool(self, tool_name: str, arguments: dict):
43
- """Rufe ein spezifisches Tool auf dem MCP-Server auf"""
44
  try:
45
- if not self.session:
46
- await self.connect()
 
 
 
 
 
 
 
 
 
 
47
 
48
- result = await self.session.call_tool(tool_name, arguments)
49
- return result
 
 
 
 
 
 
 
 
 
 
50
  except Exception as e:
51
  print(f"Fehler beim Tool-Aufruf: {e}")
52
  return None
@@ -63,42 +116,58 @@ class MCPTrainClient:
63
  }
64
  )
65
 
66
- if result and hasattr(result, 'content'):
67
- return self._parse_result(result.content)
68
  return None
 
69
  except Exception as e:
70
  print(f"Fehler bei der Verbindungssuche: {e}")
71
  return None
72
 
73
- def _parse_result(self, content):
74
  """Parse die Antwort vom MCP-Server"""
75
  try:
76
- if isinstance(content, list) and len(content) > 0:
77
- # Extrahiere den Text aus der Antwort
78
- if hasattr(content[0], 'text'):
79
- return content[0].text
80
- elif isinstance(content[0], dict) and 'text' in content[0]:
81
- return content[0]['text']
82
- return str(content)
 
 
 
 
 
83
  except Exception as e:
84
  print(f"Fehler beim Parsen: {e}")
85
- return None
 
 
 
 
86
 
87
 
88
  # Globaler MCP-Client
89
- mcp_client = None
90
- MCP_SERVER_URL = "https://mgokg-db-timetable-api.hf.space/gradio_api/mcp/"
91
 
92
 
93
  async def initialize_mcp_client():
94
  """Initialisiere den MCP-Client"""
95
  global mcp_client
96
  try:
97
- mcp_client = MCPTrainClient(MCP_SERVER_URL)
98
- await mcp_client.connect()
99
- return "MCP-Client erfolgreich initialisiert"
 
 
 
 
 
100
  except Exception as e:
101
- return f"Fehler bei der Initialisierung: {e}"
 
102
 
103
 
104
  async def search_train_connections_async(user_input: str):
@@ -107,42 +176,83 @@ async def search_train_connections_async(user_input: str):
107
 
108
  # Initialisiere Client falls noch nicht geschehen
109
  if mcp_client is None:
110
- await initialize_mcp_client()
 
 
111
 
112
  try:
113
  # Extrahiere Start und Ziel aus der Benutzereingabe
114
- # Einfache Parsing-Logik (kann erweitert werden)
115
- parts = user_input.lower().replace("von", "").replace("nach", "").strip().split()
116
 
117
- if len(parts) < 2:
118
- return "Bitte geben Sie Start- und Zielort an (z.B. 'Frankfurt nach Berlin')"
119
 
120
- # Versuche, Start und Ziel zu identifizieren
121
- if "nach" in user_input.lower():
122
- split_parts = user_input.lower().split("nach")
123
- origin = split_parts[0].replace("von", "").strip()
124
- destination = split_parts[1].strip()
 
 
 
125
  else:
126
- origin = parts[0].capitalize()
127
- destination = parts[-1].capitalize()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
  # Rufe MCP-Server auf
130
  result = await mcp_client.search_connections(origin, destination)
131
 
132
  if result:
133
  # Formatiere Ergebnis als Markdown
134
- response = f"## Zugverbindungen von {origin} nach {destination}\n\n"
135
  response += result
136
  return response
137
  else:
138
- return "Keine Verbindungen gefunden oder Fehler bei der Abfrage."
 
 
 
 
 
 
 
139
 
140
  except Exception as e:
141
- return f"Fehler bei der Suche: {str(e)}\n\nBitte versuchen Sie es erneut oder prüfen Sie Ihre Eingabe."
 
 
 
 
 
 
 
 
142
 
143
 
144
  def search_train_connections(user_input: str):
145
  """Synchrone Wrapper-Funktion für Gradio"""
 
 
 
146
  try:
147
  # Erstelle neuen Event Loop für die asynchrone Funktion
148
  loop = asyncio.new_event_loop()
@@ -151,12 +261,15 @@ def search_train_connections(user_input: str):
151
  loop.close()
152
  return result, ""
153
  except Exception as e:
154
- return f"Fehler: {str(e)}", ""
155
 
156
 
157
  if __name__ == '__main__':
158
  # Initialisiere MCP-Client beim Start
159
- print("Initialisiere MCP-Client...")
 
 
 
160
  try:
161
  loop = asyncio.new_event_loop()
162
  asyncio.set_event_loop(loop)
@@ -164,28 +277,43 @@ if __name__ == '__main__':
164
  print(init_result)
165
  loop.close()
166
  except Exception as e:
167
- print(f"Warnung bei Initialisierung: {e}")
168
  print("Client wird bei erster Anfrage initialisiert.")
169
 
 
 
170
  # Gradio Interface
171
- with gr.Blocks() as demo:
172
- title = gr.Markdown("# 🚂 Deutsche Bahn Zugverbindungen (MCP-Client)")
 
 
173
  description = gr.Markdown("""
174
- Geben Sie Start- und Zielort ein, um Zugverbindungen zu finden.
175
 
176
- **Beispiele:**
177
- - Frankfurt nach Berlin
178
- - Von München nach Hamburg
179
- - Köln Stuttgart
 
180
  """)
181
 
182
- output_textbox = gr.Markdown()
183
- input_textbox = gr.Textbox(
184
- lines=2,
185
- label="Ihre Anfrage",
186
- placeholder="z.B. 'Frankfurt nach Berlin' oder 'Von München nach Hamburg'"
187
- )
188
- submit_button = gr.Button("🔍 Verbindungen suchen")
 
 
 
 
 
 
 
 
 
 
189
 
190
  # Event Handler
191
  submit_button.click(
 
2
  import os
3
  import json
4
  import asyncio
 
 
 
5
  import httpx
6
+ from typing import Optional, Dict, Any
7
 
8
+ class MCPHTTPClient:
9
+ """MCP Client für Streamable HTTP Transport"""
10
 
11
  def __init__(self, server_url: str):
12
+ self.server_url = server_url.rstrip('/')
 
13
  self.tools = []
14
+ self.client = httpx.AsyncClient(timeout=30.0)
15
 
16
+ async def initialize(self):
17
+ """Initialisiere den Client und hole verfügbare Tools"""
18
  try:
19
+ # MCP über HTTP: Sende initialize request
20
+ response = await self.client.post(
21
+ f"{self.server_url}/initialize",
22
+ json={
23
+ "jsonrpc": "2.0",
24
+ "id": 1,
25
+ "method": "initialize",
26
+ "params": {
27
+ "protocolVersion": "2024-11-05",
28
+ "capabilities": {},
29
+ "clientInfo": {
30
+ "name": "gradio-mcp-client",
31
+ "version": "1.0.0"
32
+ }
33
+ }
34
+ }
35
+ )
36
 
37
+ if response.status_code == 200:
38
+ print("MCP-Server initialisiert")
39
+ await self.list_tools()
40
+ return True
41
+ else:
42
+ print(f"Initialisierung fehlgeschlagen: {response.status_code}")
43
+ return False
44
+
45
  except Exception as e:
46
+ print(f"Verbindungsfehler bei Initialisierung: {e}")
47
  return False
48
 
49
  async def list_tools(self):
50
  """Rufe verfügbare Tools vom MCP-Server ab"""
51
  try:
52
+ response = await self.client.post(
53
+ f"{self.server_url}/list_tools",
54
+ json={
55
+ "jsonrpc": "2.0",
56
+ "id": 2,
57
+ "method": "tools/list",
58
+ "params": {}
59
+ }
60
+ )
61
+
62
+ if response.status_code == 200:
63
+ data = response.json()
64
+ if "result" in data and "tools" in data["result"]:
65
+ self.tools = data["result"]["tools"]
66
+ print(f"Verfügbare Tools: {[tool['name'] for tool in self.tools]}")
67
+ else:
68
+ print("Keine Tools gefunden")
69
+ else:
70
+ print(f"Fehler beim Abrufen der Tools: {response.status_code}")
71
+
72
  except Exception as e:
73
  print(f"Fehler beim Abrufen der Tools: {e}")
74
 
75
+ async def call_tool(self, tool_name: str, arguments: Dict[str, Any]):
76
+ """Rufe ein Tool auf dem MCP-Server auf"""
77
  try:
78
+ response = await self.client.post(
79
+ f"{self.server_url}/call_tool",
80
+ json={
81
+ "jsonrpc": "2.0",
82
+ "id": 3,
83
+ "method": "tools/call",
84
+ "params": {
85
+ "name": tool_name,
86
+ "arguments": arguments
87
+ }
88
+ }
89
+ )
90
 
91
+ if response.status_code == 200:
92
+ data = response.json()
93
+ if "result" in data:
94
+ return data["result"]
95
+ else:
96
+ print(f"Unerwartete Antwort: {data}")
97
+ return None
98
+ else:
99
+ print(f"Tool-Aufruf fehlgeschlagen: {response.status_code}")
100
+ print(f"Response: {response.text}")
101
+ return None
102
+
103
  except Exception as e:
104
  print(f"Fehler beim Tool-Aufruf: {e}")
105
  return None
 
116
  }
117
  )
118
 
119
+ if result:
120
+ return self._parse_result(result)
121
  return None
122
+
123
  except Exception as e:
124
  print(f"Fehler bei der Verbindungssuche: {e}")
125
  return None
126
 
127
+ def _parse_result(self, result):
128
  """Parse die Antwort vom MCP-Server"""
129
  try:
130
+ # MCP-Server gibt Ergebnisse im content-Array zurück
131
+ if isinstance(result, dict) and "content" in result:
132
+ content = result["content"]
133
+ if isinstance(content, list) and len(content) > 0:
134
+ if isinstance(content[0], dict) and "text" in content[0]:
135
+ return content[0]["text"]
136
+ elif hasattr(content[0], 'text'):
137
+ return content[0].text
138
+ return str(content)
139
+
140
+ return str(result)
141
+
142
  except Exception as e:
143
  print(f"Fehler beim Parsen: {e}")
144
+ return str(result)
145
+
146
+ async def close(self):
147
+ """Schließe die HTTP-Verbindung"""
148
+ await self.client.aclose()
149
 
150
 
151
  # Globaler MCP-Client
152
+ mcp_client: Optional[MCPHTTPClient] = None
153
+ MCP_SERVER_URL = "https://mgokg-db-timetable-api.hf.space/gradio_api/mcp"
154
 
155
 
156
  async def initialize_mcp_client():
157
  """Initialisiere den MCP-Client"""
158
  global mcp_client
159
  try:
160
+ mcp_client = MCPHTTPClient(MCP_SERVER_URL)
161
+ success = await mcp_client.initialize()
162
+
163
+ if success:
164
+ return "✅ MCP-Client erfolgreich initialisiert"
165
+ else:
166
+ return "⚠️ MCP-Client initialisiert, aber keine Tools verfügbar"
167
+
168
  except Exception as e:
169
+ print(f"Fehler bei der Initialisierung: {e}")
170
+ return f"❌ Fehler bei der Initialisierung: {e}"
171
 
172
 
173
  async def search_train_connections_async(user_input: str):
 
176
 
177
  # Initialisiere Client falls noch nicht geschehen
178
  if mcp_client is None:
179
+ init_msg = await initialize_mcp_client()
180
+ if "❌" in init_msg:
181
+ return f"{init_msg}\n\nBitte versuchen Sie es später erneut."
182
 
183
  try:
184
  # Extrahiere Start und Ziel aus der Benutzereingabe
185
+ user_input_lower = user_input.lower().strip()
 
186
 
187
+ # Verschiedene Parsing-Strategien
188
+ origin, destination = None, None
189
 
190
+ if " nach " in user_input_lower:
191
+ parts = user_input_lower.split(" nach ")
192
+ origin = parts[0].replace("von", "").strip()
193
+ destination = parts[1].strip()
194
+ elif " - " in user_input_lower:
195
+ parts = user_input_lower.split(" - ")
196
+ origin = parts[0].strip()
197
+ destination = parts[1].strip()
198
  else:
199
+ # Versuche einfache Tokenisierung
200
+ words = user_input_lower.replace("von", "").replace(",", "").split()
201
+ if len(words) >= 2:
202
+ origin = words[0]
203
+ destination = words[-1]
204
+
205
+ if not origin or not destination:
206
+ return """❓ Bitte geben Sie Start- und Zielort an.
207
+
208
+ **Beispiele:**
209
+ - Frankfurt nach Berlin
210
+ - Von München nach Hamburg
211
+ - Köln - Stuttgart
212
+ - Hamburg Berlin"""
213
+
214
+ # Kapitalisiere die Namen
215
+ origin = origin.capitalize()
216
+ destination = destination.capitalize()
217
+
218
+ # Zeige Suche an
219
+ print(f"Suche Verbindungen: {origin} → {destination}")
220
 
221
  # Rufe MCP-Server auf
222
  result = await mcp_client.search_connections(origin, destination)
223
 
224
  if result:
225
  # Formatiere Ergebnis als Markdown
226
+ response = f"## 🚂 Zugverbindungen von {origin} nach {destination}\n\n"
227
  response += result
228
  return response
229
  else:
230
+ return f"""⚠️ Keine Verbindungen gefunden oder Fehler bei der Abfrage.
231
+
232
+ **Gesuchte Route:** {origin} → {destination}
233
+
234
+ Bitte prüfen Sie:
235
+ - Sind die Ortsnamen korrekt geschrieben?
236
+ - Existieren die Bahnhöfe?
237
+ - Versuchen Sie es mit vollständigen Stadtnamen"""
238
 
239
  except Exception as e:
240
+ error_msg = str(e)
241
+ print(f"Fehler bei der Suche: {error_msg}")
242
+ return f"""❌ Fehler bei der Suche: {error_msg}
243
+
244
+ Bitte versuchen Sie es erneut oder verwenden Sie ein anderes Format.
245
+
246
+ **Beispiele:**
247
+ - Frankfurt nach Berlin
248
+ - Von München nach Hamburg"""
249
 
250
 
251
  def search_train_connections(user_input: str):
252
  """Synchrone Wrapper-Funktion für Gradio"""
253
+ if not user_input or not user_input.strip():
254
+ return "Bitte geben Sie eine Anfrage ein.", ""
255
+
256
  try:
257
  # Erstelle neuen Event Loop für die asynchrone Funktion
258
  loop = asyncio.new_event_loop()
 
261
  loop.close()
262
  return result, ""
263
  except Exception as e:
264
+ return f"Fehler: {str(e)}", ""
265
 
266
 
267
  if __name__ == '__main__':
268
  # Initialisiere MCP-Client beim Start
269
+ print("=" * 60)
270
+ print("🚂 Deutsche Bahn MCP-Client wird gestartet...")
271
+ print("=" * 60)
272
+
273
  try:
274
  loop = asyncio.new_event_loop()
275
  asyncio.set_event_loop(loop)
 
277
  print(init_result)
278
  loop.close()
279
  except Exception as e:
280
+ print(f"⚠️ Warnung bei Initialisierung: {e}")
281
  print("Client wird bei erster Anfrage initialisiert.")
282
 
283
+ print("=" * 60)
284
+
285
  # Gradio Interface
286
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
287
+ title = gr.Markdown("# 🚂 Deutsche Bahn Zugverbindungen")
288
+ gr.Markdown("### MCP-Client für DB Timetable API")
289
+
290
  description = gr.Markdown("""
291
+ Geben Sie Start- und Zielort ein, um aktuelle Zugverbindungen zu finden.
292
 
293
+ **Beispiele für Eingaben:**
294
+ - `Frankfurt nach Berlin`
295
+ - `Von München nach Hamburg`
296
+ - `Köln - Stuttgart`
297
+ - `Hamburg Berlin`
298
  """)
299
 
300
+ with gr.Row():
301
+ with gr.Column(scale=4):
302
+ input_textbox = gr.Textbox(
303
+ lines=2,
304
+ label="Ihre Anfrage",
305
+ placeholder="z.B. 'Frankfurt nach Berlin' oder 'Von München nach Hamburg'",
306
+ autofocus=True
307
+ )
308
+ with gr.Column(scale=1):
309
+ submit_button = gr.Button("🔍 Suchen", variant="primary", size="lg")
310
+
311
+ output_textbox = gr.Markdown(label="Ergebnisse")
312
+
313
+ gr.Markdown("""
314
+ ---
315
+ 💡 **Hinweis:** Dieser Client nutzt die Deutsche Bahn Timetable API über einen MCP-Server.
316
+ """)
317
 
318
  # Event Handler
319
  submit_button.click(