mgokg commited on
Commit
5d6f0b5
·
verified ·
1 Parent(s): 57e46a9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +116 -87
app.py CHANGED
@@ -7,16 +7,12 @@ from google.genai import types
7
  from mcp import ClientSession, StdioServerParameters
8
  from mcp.client.stdio import stdio_client
9
 
10
- # 1. Konfiguration des MCP-Servers via STDIO-Bridge
11
- # FIX: Changed transport from 'streamable-http' to 'http-first' based on error log
12
  server_params = StdioServerParameters(
13
  command="npx",
14
  args=[
15
- "-y", # Auto-confirm installation if needed
16
  "mcp-remote",
17
- "https://mgokg-db-timetable-api.hf.space/gradio_api/mcp/",
18
- "--transport",
19
- "http-first"
20
  ]
21
  )
22
 
@@ -26,96 +22,129 @@ async def generate(input_text):
26
  except Exception as e:
27
  return f"Fehler bei der Initialisierung: {e}", ""
28
 
29
- model = "gemini-2.5-flash"
30
-
31
- # 2. Aufbau der MCP-Session
32
- # Note: This context manager keeps the connection open only during the generation
33
- async with stdio_client(server_params) as (read, write):
34
- async with ClientSession(read, write) as session:
35
- await session.initialize()
36
-
37
- # MCP-Tools abrufen und in Gemini-Format konvertieren
38
- mcp_tools_data = await session.list_tools()
39
- mcp_declarations = [
40
- {
41
- "name": tool.name,
42
- "description": tool.description or "Ruft Zugverbindungen ab.",
43
- "parameters": tool.inputSchema,
44
- }
45
- for tool in mcp_tools_data.tools
46
- ]
47
-
48
- # 3. Kombination der Tools
49
- # Using function_declarations disables Automatic Function Calling (AFC),
50
- # which is what we want because we handle the loop manually below.
51
- tools = [
52
- types.Tool(google_search=types.GoogleSearch()),
53
- types.Tool(function_declarations=mcp_declarations)
54
- ]
55
-
56
- contents = [types.Content(role="user", parts=[types.Part.from_text(text=input_text)])]
57
-
58
- # Erster Aufruf an das Modell
59
- response = await client.aio.models.generate_content(
60
- model=model,
61
- contents=contents,
62
- config=types.GenerateContentConfig(tools=tools, temperature=0.4)
63
- )
64
-
65
- # 4. Agentic Loop: Bearbeitung von Tool-Calls
66
- turn_count = 0
67
-
68
- # Note: response.function_calls is a helper property in the SDK
69
- while response.function_calls and turn_count < 5:
70
- turn_count += 1
71
 
72
- # FIX: response.candidates is a list. Must access index 0.
73
- contents.append(response.candidates[0].content)
74
 
75
- tool_responses = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
- for fc in response.function_calls:
78
- try:
79
- # Ausführung des MCP-Tools
80
- tool_result = await session.call_tool(fc.name, fc.args)
81
-
82
- # Ergebnis formatieren
83
- # Check if content is a list (standard MCP) or text
84
- if hasattr(tool_result.content, 'text'):
85
- # Some implementations might differ, standardizing to string
86
- result_data = tool_result.content.text
87
- elif isinstance(tool_result.content, list):
88
- result_data = "\n".join([item.text for item in tool_result.content if hasattr(item, 'text')])
89
- else:
90
- result_data = str(tool_result.content)
91
-
92
- tool_responses.append(types.Part.from_function_response(
93
- name=fc.name, response={"result": result_data}
94
- ))
95
- except Exception as e:
96
- tool_responses.append(types.Part.from_function_response(
97
- name=fc.name, response={"error": str(e)}
98
- ))
99
-
100
- contents.append(types.Content(role="user", parts=tool_responses))
101
 
102
- # Nächster Modell-Aufruf mit den Tool-Ergebnissen
103
  response = await client.aio.models.generate_content(
104
- model=model, contents=contents, config=types.GenerateContentConfig(tools=tools)
 
 
 
 
 
105
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
 
107
- return response.text, ""
108
-
109
- # 5. Gradio UI Integration
110
- # FIX: Made this async and removed asyncio.run() to prevent Event Loop conflicts
111
- async def ui_wrapper(input_text):
112
- return await generate(input_text)
113
 
114
  if __name__ == '__main__':
115
  with gr.Blocks() as demo:
116
- gr.Markdown("# Gemini 2.0 Flash + Search + DB Timetable")
117
  output_textbox = gr.Markdown()
118
- input_textbox = gr.Textbox(lines=3, label="Anfrage", placeholder="z.B. Wie komme ich von Berlin nach Hamburg?")
 
 
 
 
119
  submit_button = gr.Button("Senden")
120
 
121
  submit_button.click(
@@ -123,8 +152,8 @@ if __name__ == '__main__':
123
  inputs=input_textbox,
124
  outputs=[output_textbox, input_textbox]
125
  )
126
- demo.launch(show_error=True, ssr_mode=False)
127
-
128
 
129
  """
130
  import base64
 
7
  from mcp import ClientSession, StdioServerParameters
8
  from mcp.client.stdio import stdio_client
9
 
10
+ # MCP-Server Konfiguration (ohne ungültigen Transport-Parameter)
 
11
  server_params = StdioServerParameters(
12
  command="npx",
13
  args=[
 
14
  "mcp-remote",
15
+ "https://mgokg-db-timetable-api.hf.space/gradio_api/mcp/"
 
 
16
  ]
17
  )
18
 
 
22
  except Exception as e:
23
  return f"Fehler bei der Initialisierung: {e}", ""
24
 
25
+ model = "gemini-2.0-flash-exp"
26
+
27
+ try:
28
+ # MCP-Session aufbauen
29
+ async with stdio_client(server_params) as (read, write):
30
+ async with ClientSession(read, write) as session:
31
+ await session.initialize()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
+ # MCP-Tools abrufen
34
+ mcp_tools_data = await session.list_tools()
35
 
36
+ # Tools in Gemini-kompatibles Format konvertieren
37
+ mcp_declarations = []
38
+ for tool in mcp_tools_data.tools:
39
+ # Sicherstellen, dass inputSchema korrekt formatiert ist
40
+ schema = tool.inputSchema if tool.inputSchema else {
41
+ "type": "object",
42
+ "properties": {},
43
+ "required": []
44
+ }
45
+
46
+ mcp_declarations.append(
47
+ types.FunctionDeclaration(
48
+ name=tool.name,
49
+ description=tool.description or "MCP Tool",
50
+ parameters=schema
51
+ )
52
+ )
53
+
54
+ # Tools kombinieren: Google Search + MCP Tools
55
+ tools = [types.Tool(google_search=types.GoogleSearch())]
56
 
57
+ if mcp_declarations:
58
+ tools.append(types.Tool(function_declarations=mcp_declarations))
59
+
60
+ contents = [types.Content(
61
+ role="user",
62
+ parts=[types.Part.from_text(text=input_text)]
63
+ )]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
+ # Erster API-Aufruf
66
  response = await client.aio.models.generate_content(
67
+ model=model,
68
+ contents=contents,
69
+ config=types.GenerateContentConfig(
70
+ tools=tools,
71
+ temperature=0.4
72
+ )
73
  )
74
+
75
+ # Agentic Loop für Tool-Calls
76
+ turn_count = 0
77
+ max_turns = 5
78
+
79
+ while hasattr(response.candidates[0].content, 'parts') and turn_count < max_turns:
80
+ function_calls = [
81
+ part for part in response.candidates[0].content.parts
82
+ if hasattr(part, 'function_call') and part.function_call
83
+ ]
84
+
85
+ if not function_calls:
86
+ break
87
+
88
+ turn_count += 1
89
+ contents.append(response.candidates[0].content)
90
+ tool_responses = []
91
+
92
+ for part in function_calls:
93
+ fc = part.function_call
94
+ try:
95
+ # MCP-Tool ausführen
96
+ tool_result = await session.call_tool(fc.name, dict(fc.args))
97
+
98
+ # Ergebnis formatieren
99
+ if tool_result.isError:
100
+ result_text = f"Error: {tool_result.content[0].text if tool_result.content else 'Unknown error'}"
101
+ else:
102
+ result_text = tool_result.content[0].text if tool_result.content else "No result"
103
+
104
+ tool_responses.append(
105
+ types.Part.from_function_response(
106
+ name=fc.name,
107
+ response={"result": result_text}
108
+ )
109
+ )
110
+ except Exception as e:
111
+ tool_responses.append(
112
+ types.Part.from_function_response(
113
+ name=fc.name,
114
+ response={"error": str(e)}
115
+ )
116
+ )
117
+
118
+ contents.append(types.Content(role="function", parts=tool_responses))
119
+
120
+ # Nächster API-Aufruf
121
+ response = await client.aio.models.generate_content(
122
+ model=model,
123
+ contents=contents,
124
+ config=types.GenerateContentConfig(tools=tools, temperature=0.4)
125
+ )
126
+
127
+ return response.text, ""
128
+
129
+ except Exception as e:
130
+ return f"Fehler während der Verarbeitung: {str(e)}", ""
131
 
132
+ # Gradio UI Wrapper
133
+ def ui_wrapper(input_text):
134
+ try:
135
+ return asyncio.run(generate(input_text))
136
+ except Exception as e:
137
+ return f"UI Fehler: {str(e)}", ""
138
 
139
  if __name__ == '__main__':
140
  with gr.Blocks() as demo:
141
+ gr.Markdown("# Gemini 2.0 Flash + Google Search + DB Timetable (MCP)")
142
  output_textbox = gr.Markdown()
143
+ input_textbox = gr.Textbox(
144
+ lines=3,
145
+ label="Anfrage",
146
+ placeholder="z.B. Wie komme ich von Berlin nach Hamburg?"
147
+ )
148
  submit_button = gr.Button("Senden")
149
 
150
  submit_button.click(
 
152
  inputs=input_textbox,
153
  outputs=[output_textbox, input_textbox]
154
  )
155
+
156
+ demo.launch(show_error=True)
157
 
158
  """
159
  import base64