ryomo commited on
Commit
276be42
·
1 Parent(s): 208e67d

feat: integrate tool prefix handling for MCP tools in chat functionality

Browse files
app.py CHANGED
@@ -23,6 +23,7 @@ from unpredictable_lord.mcp_server.mcp_server import (
23
  list_available_advice,
24
  )
25
  from unpredictable_lord.settings import ENABLE_GRADIO_DEPRECATION_WARNING, LOGGING_LEVEL
 
26
 
27
  # Configure logging level from environment variable
28
  log_level = getattr(logging, LOGGING_LEVEL, logging.INFO)
@@ -52,6 +53,10 @@ NO_GAME_MESSAGE = """_No active game._
52
  2. Click "⚔️ Start New Game"
53
  """
54
 
 
 
 
 
55
 
56
  # Gradio UI
57
  with gr.Blocks(title="Unpredictable Lord") as demo:
@@ -145,12 +150,6 @@ with gr.Blocks(title="Unpredictable Lord") as demo:
145
  # Helper functions for Chat tab
146
  async def start_chat_game(personality: str):
147
  """Start a new game via MCP client and return updated UI state."""
148
- # Get tool prefix from Gradio's MCP server (handles HF Spaces naming)
149
- tool_prefix = ""
150
- if demo.mcp_server_obj:
151
- tool_prefix = demo.mcp_server_obj.tool_prefix.replace("-", "_")
152
- logger.info(f"Using tool prefix: '{tool_prefix}'")
153
-
154
  # Call init_game via MCP client
155
  mcp_client = MCPClient(tool_prefix=tool_prefix)
156
  result_json = await mcp_client.call_tool(
@@ -298,7 +297,7 @@ _{personality_desc}_"""
298
  history_for_model = history[:-1]
299
 
300
  async for updated_history in chat_with_mcp_tools(
301
- user_message, history_for_model, system_instructions
302
  ):
303
  yield updated_history
304
 
 
23
  list_available_advice,
24
  )
25
  from unpredictable_lord.settings import ENABLE_GRADIO_DEPRECATION_WARNING, LOGGING_LEVEL
26
+ from unpredictable_lord.utils import get_tool_prefix
27
 
28
  # Configure logging level from environment variable
29
  log_level = getattr(logging, LOGGING_LEVEL, logging.INFO)
 
53
  2. Click "⚔️ Start New Game"
54
  """
55
 
56
+ # MCP tool prefix for HF Spaces
57
+ tool_prefix = get_tool_prefix()
58
+ logger.info(f"Using tool prefix: '{tool_prefix}'")
59
+
60
 
61
  # Gradio UI
62
  with gr.Blocks(title="Unpredictable Lord") as demo:
 
150
  # Helper functions for Chat tab
151
  async def start_chat_game(personality: str):
152
  """Start a new game via MCP client and return updated UI state."""
 
 
 
 
 
 
153
  # Call init_game via MCP client
154
  mcp_client = MCPClient(tool_prefix=tool_prefix)
155
  result_json = await mcp_client.call_tool(
 
297
  history_for_model = history[:-1]
298
 
299
  async for updated_history in chat_with_mcp_tools(
300
+ user_message, history_for_model, system_instructions, tool_prefix
301
  ):
302
  yield updated_history
303
 
src/unpredictable_lord/chat/chat.py CHANGED
@@ -135,6 +135,7 @@ async def chat_with_mcp_tools(
135
  user_message: str,
136
  chat_history: list[dict[str, str]],
137
  system_instructions: str,
 
138
  ) -> AsyncGenerator[list[dict], None]:
139
  """
140
  Chat with LLM with MCP tool support (async streaming version).
@@ -211,7 +212,7 @@ async def chat_with_mcp_tools(
211
  yield partial_history
212
 
213
  # Execute tools via MCP
214
- tool_result_messages = await execute_tool_calls(tool_calls)
215
 
216
  messages.extend(tool_result_messages)
217
 
 
135
  user_message: str,
136
  chat_history: list[dict[str, str]],
137
  system_instructions: str,
138
+ tool_prefix: str,
139
  ) -> AsyncGenerator[list[dict], None]:
140
  """
141
  Chat with LLM with MCP tool support (async streaming version).
 
212
  yield partial_history
213
 
214
  # Execute tools via MCP
215
+ tool_result_messages = await execute_tool_calls(tool_calls, tool_prefix)
216
 
217
  messages.extend(tool_result_messages)
218
 
src/unpredictable_lord/chat/chat_tools.py CHANGED
@@ -14,6 +14,7 @@ from unpredictable_lord.chat.mcp_client import MCPClient
14
 
15
  logger = logging.getLogger(__name__)
16
 
 
17
  # Tool definitions matching MCP tools in mcp_tools.py
18
  TOOL_DEFINITIONS = [
19
  oh.ToolDescription(
@@ -123,11 +124,12 @@ def extract_tool_calls(parsed_messages: list[oh.Message]) -> list[oh.Message]:
123
  ]
124
 
125
 
126
- async def _execute_tool(tool_name: str, params: dict) -> str:
127
  """Execute a single MCP tool and return the result as JSON string.
128
 
129
  Args:
130
  tool_name: Name of the tool to execute.
 
131
  params: Tool parameters as a dictionary.
132
 
133
  Returns:
@@ -135,7 +137,7 @@ async def _execute_tool(tool_name: str, params: dict) -> str:
135
  """
136
  import ast
137
 
138
- mcp_client = MCPClient()
139
  try:
140
  result = await mcp_client.call_tool(tool_name, params)
141
 
@@ -162,11 +164,14 @@ async def _execute_tool(tool_name: str, params: dict) -> str:
162
  return json.dumps({"error": str(e)}, ensure_ascii=False)
163
 
164
 
165
- async def execute_tool_calls(tool_calls: list[oh.Message]) -> list[oh.Message]:
 
 
166
  """Execute tool calls and return result messages.
167
 
168
  Args:
169
  tool_calls: List of tool call messages extracted from parser.
 
170
 
171
  Returns:
172
  List of tool result messages to be added to conversation.
@@ -189,7 +194,7 @@ async def execute_tool_calls(tool_calls: list[oh.Message]) -> list[oh.Message]:
189
  logger.info(f"Executing MCP tool: {tool_name} with params: {params}")
190
 
191
  # Execute tool via MCP (returns JSON string)
192
- result_json = await _execute_tool(tool_name, params)
193
 
194
  logger.info(f"Tool {tool_name} result: {result_json}")
195
 
 
14
 
15
  logger = logging.getLogger(__name__)
16
 
17
+
18
  # Tool definitions matching MCP tools in mcp_tools.py
19
  TOOL_DEFINITIONS = [
20
  oh.ToolDescription(
 
124
  ]
125
 
126
 
127
+ async def _execute_tool(tool_name: str, tool_prefix: str, params: dict) -> str:
128
  """Execute a single MCP tool and return the result as JSON string.
129
 
130
  Args:
131
  tool_name: Name of the tool to execute.
132
+ tool_prefix: Prefix to use for the tool name.
133
  params: Tool parameters as a dictionary.
134
 
135
  Returns:
 
137
  """
138
  import ast
139
 
140
+ mcp_client = MCPClient(tool_prefix=tool_prefix)
141
  try:
142
  result = await mcp_client.call_tool(tool_name, params)
143
 
 
164
  return json.dumps({"error": str(e)}, ensure_ascii=False)
165
 
166
 
167
+ async def execute_tool_calls(
168
+ tool_calls: list[oh.Message], tool_prefix: str
169
+ ) -> list[oh.Message]:
170
  """Execute tool calls and return result messages.
171
 
172
  Args:
173
  tool_calls: List of tool call messages extracted from parser.
174
+ tool_prefix: Prefix to use for the tool names.
175
 
176
  Returns:
177
  List of tool result messages to be added to conversation.
 
194
  logger.info(f"Executing MCP tool: {tool_name} with params: {params}")
195
 
196
  # Execute tool via MCP (returns JSON string)
197
+ result_json = await _execute_tool(tool_name, tool_prefix, params)
198
 
199
  logger.info(f"Tool {tool_name} result: {result_json}")
200
 
src/unpredictable_lord/utils.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+
4
+ def get_tool_prefix() -> str:
5
+ """Get the MCP tool prefix for HuggingFace Spaces.
6
+
7
+ Gradio adds a prefix based on the Space ID. This function replicates
8
+ that logic to ensure tool names match.
9
+
10
+ Returns:
11
+ Tool prefix string (empty on local, "space_name_" on HF Spaces).
12
+ """
13
+ space_id = os.environ.get("SPACE_ID", "")
14
+ if space_id:
15
+ prefix = space_id.split("/")[-1] + "_"
16
+ prefix = prefix.replace("-", "_")
17
+ return prefix
18
+ return ""