Peter Larnholt commited on
Commit
1f77df0
·
1 Parent(s): 80b0386

Add simple tool calling script that works around vLLM tool_choice limitation

Browse files
Files changed (2) hide show
  1. TOOL_CALLING_GUIDE.md +134 -0
  2. simple_tool_chat.py +197 -0
TOOL_CALLING_GUIDE.md ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Tool Calling Guide
2
+
3
+ Your ExCom AI deployment supports tool calling! However, there's a quirk with vLLM that requires a workaround.
4
+
5
+ ## The Issue
6
+
7
+ vLLM requires `--enable-auto-tool-choice` and `--tool-call-parser` flags to accept the `tool_choice: "auto"` parameter. Since Qwen 2.5 has native tool calling built into the model, we don't use these flags.
8
+
9
+ **Result**: LangChain's default agent framework sends `tool_choice: "auto"` which vLLM rejects with a 400 error.
10
+
11
+ ## Solution: Use OpenAI SDK Directly
12
+
13
+ I've created `simple_tool_chat.py` which uses the OpenAI SDK directly and doesn't send `tool_choice`.
14
+
15
+ ### Installation
16
+
17
+ ```bash
18
+ pip install openai
19
+ ```
20
+
21
+ ### Usage
22
+
23
+ ```bash
24
+ python simple_tool_chat.py
25
+ ```
26
+
27
+ ### Example Session
28
+
29
+ ```
30
+ You: What is 15 * 23 + 100?
31
+ 🔧 Calling tool: calculator({'expression': '15 * 23 + 100'})
32
+ Assistant: The result is 445.
33
+
34
+ You: What's the weather in Paris and what time is it?
35
+ 🔧 Calling tool: get_weather({'city': 'Paris'})
36
+ 🔧 Calling tool: get_current_time({})
37
+ Assistant: The weather in Paris is 18°C and sunny. The current time is 2025-10-09 18:30:45.
38
+ ```
39
+
40
+ ## How It Works
41
+
42
+ 1. **No tool_choice parameter** - We don't send `tool_choice` at all
43
+ 2. **Qwen decides naturally** - The model's training handles when to use tools
44
+ 3. **OpenAI SDK** - Direct HTTP calls to your vLLM endpoint
45
+ 4. **Multi-turn** - Maintains conversation history for context
46
+
47
+ ## Using with Your Own Code
48
+
49
+ ```python
50
+ from openai import OpenAI
51
+
52
+ client = OpenAI(
53
+ base_url="https://plarnholt-excom-ai-demo.hf.space/v1",
54
+ api_key="not-needed"
55
+ )
56
+
57
+ # Define your tools
58
+ tools = [{
59
+ "type": "function",
60
+ "function": {
61
+ "name": "my_tool",
62
+ "description": "What it does",
63
+ "parameters": {
64
+ "type": "object",
65
+ "properties": {
66
+ "param": {"type": "string"}
67
+ }
68
+ }
69
+ }
70
+ }]
71
+
72
+ # Call without tool_choice parameter
73
+ response = client.chat.completions.create(
74
+ model="excom-ai",
75
+ messages=[{"role": "user", "content": "Use my tool"}],
76
+ tools=tools,
77
+ temperature=0.4
78
+ # NOTE: No tool_choice parameter!
79
+ )
80
+
81
+ # Check for tool calls
82
+ if response.choices[0].message.tool_calls:
83
+ for tool_call in response.choices[0].message.tool_calls:
84
+ print(f"Tool: {tool_call.function.name}")
85
+ print(f"Args: {tool_call.function.arguments}")
86
+ ```
87
+
88
+ ## Adding Custom Tools
89
+
90
+ Edit `simple_tool_chat.py`:
91
+
92
+ ```python
93
+ # 1. Add tool definition to 'tools' list
94
+ {
95
+ "type": "function",
96
+ "function": {
97
+ "name": "my_custom_tool",
98
+ "description": "What it does",
99
+ "parameters": {
100
+ "type": "object",
101
+ "properties": {
102
+ "param": {"type": "string", "description": "Param description"}
103
+ },
104
+ "required": ["param"]
105
+ }
106
+ }
107
+ }
108
+
109
+ # 2. Add implementation
110
+ def my_custom_tool(param: str) -> str:
111
+ # Your logic here
112
+ return "result"
113
+
114
+ # 3. Add to dispatcher
115
+ def execute_tool(tool_name: str, arguments: dict) -> str:
116
+ # ... existing tools ...
117
+ elif tool_name == "my_custom_tool":
118
+ return my_custom_tool(arguments["param"])
119
+ ```
120
+
121
+ ## Troubleshooting
122
+
123
+ **Error: "auto" tool choice requires --enable-auto-tool-choice**
124
+ - You're using LangChain's agent framework
125
+ - Solution: Use `simple_tool_chat.py` instead
126
+
127
+ **Tool calls not working**
128
+ - Make sure your Space is running: https://huggingface.co/spaces/plarnholt/excom-ai-demo
129
+ - Check that you're not sending `tool_choice` parameter
130
+ - Verify tools are properly formatted (see OpenAI docs)
131
+
132
+ **500 Internal Server Error**
133
+ - Space might be sleeping - make a request to wake it up
134
+ - Check Space logs for errors
simple_tool_chat.py ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Simple tool-calling chat with ExCom AI
3
+ Works around vLLM's tool_choice requirements
4
+ """
5
+
6
+ from openai import OpenAI
7
+ import json
8
+ from datetime import datetime
9
+ import math
10
+
11
+ # Configure OpenAI client for your vLLM endpoint
12
+ client = OpenAI(
13
+ base_url="https://plarnholt-excom-ai-demo.hf.space/v1",
14
+ api_key="not-needed"
15
+ )
16
+
17
+ # Define tools
18
+ tools = [
19
+ {
20
+ "type": "function",
21
+ "function": {
22
+ "name": "calculator",
23
+ "description": "Evaluates a mathematical expression",
24
+ "parameters": {
25
+ "type": "object",
26
+ "properties": {
27
+ "expression": {
28
+ "type": "string",
29
+ "description": "Math expression to evaluate, e.g., '2 + 2 * 3'"
30
+ }
31
+ },
32
+ "required": ["expression"]
33
+ }
34
+ }
35
+ },
36
+ {
37
+ "type": "function",
38
+ "function": {
39
+ "name": "get_current_time",
40
+ "description": "Returns the current date and time",
41
+ "parameters": {
42
+ "type": "object",
43
+ "properties": {}
44
+ }
45
+ }
46
+ },
47
+ {
48
+ "type": "function",
49
+ "function": {
50
+ "name": "get_weather",
51
+ "description": "Gets the weather for a city (simulated)",
52
+ "parameters": {
53
+ "type": "object",
54
+ "properties": {
55
+ "city": {
56
+ "type": "string",
57
+ "description": "City name"
58
+ }
59
+ },
60
+ "required": ["city"]
61
+ }
62
+ }
63
+ }
64
+ ]
65
+
66
+ # Tool implementations
67
+ def calculator(expression: str) -> str:
68
+ try:
69
+ result = eval(expression, {"__builtins__": {}, "math": math})
70
+ return str(result)
71
+ except Exception as e:
72
+ return f"Error: {str(e)}"
73
+
74
+ def get_current_time() -> str:
75
+ return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
76
+
77
+ def get_weather(city: str) -> str:
78
+ weather_data = {
79
+ "paris": "18°C, sunny",
80
+ "london": "15°C, cloudy",
81
+ "new york": "22°C, partly cloudy",
82
+ "tokyo": "25°C, clear",
83
+ }
84
+ return weather_data.get(city.lower(), f"Weather data not available for {city}")
85
+
86
+ # Function dispatcher
87
+ def execute_tool(tool_name: str, arguments: dict) -> str:
88
+ if tool_name == "calculator":
89
+ return calculator(arguments["expression"])
90
+ elif tool_name == "get_current_time":
91
+ return get_current_time()
92
+ elif tool_name == "get_weather":
93
+ return get_weather(arguments["city"])
94
+ else:
95
+ return f"Unknown tool: {tool_name}"
96
+
97
+ def chat(user_message: str, messages: list = None):
98
+ """Send a message and handle tool calls"""
99
+ if messages is None:
100
+ messages = []
101
+
102
+ # Add user message
103
+ messages.append({"role": "user", "content": user_message})
104
+
105
+ # Call the model with tools (no tool_choice parameter)
106
+ response = client.chat.completions.create(
107
+ model="excom-ai",
108
+ messages=messages,
109
+ tools=tools,
110
+ temperature=0.4
111
+ )
112
+
113
+ assistant_message = response.choices[0].message
114
+
115
+ # Check if model wants to use tools
116
+ if assistant_message.tool_calls:
117
+ # Add assistant's tool call request to messages
118
+ messages.append({
119
+ "role": "assistant",
120
+ "content": assistant_message.content,
121
+ "tool_calls": [
122
+ {
123
+ "id": tc.id,
124
+ "type": "function",
125
+ "function": {
126
+ "name": tc.function.name,
127
+ "arguments": tc.function.arguments
128
+ }
129
+ }
130
+ for tc in assistant_message.tool_calls
131
+ ]
132
+ })
133
+
134
+ # Execute each tool call
135
+ for tool_call in assistant_message.tool_calls:
136
+ function_name = tool_call.function.name
137
+ function_args = json.loads(tool_call.function.arguments)
138
+
139
+ print(f"🔧 Calling tool: {function_name}({function_args})")
140
+
141
+ # Execute the tool
142
+ tool_result = execute_tool(function_name, function_args)
143
+
144
+ # Add tool result to messages
145
+ messages.append({
146
+ "role": "tool",
147
+ "tool_call_id": tool_call.id,
148
+ "name": function_name,
149
+ "content": tool_result
150
+ })
151
+
152
+ # Get final response from model
153
+ final_response = client.chat.completions.create(
154
+ model="excom-ai",
155
+ messages=messages,
156
+ temperature=0.4
157
+ )
158
+
159
+ return final_response.choices[0].message.content, messages
160
+ else:
161
+ # No tools needed, return direct response
162
+ return assistant_message.content, messages
163
+
164
+ def main():
165
+ print("=" * 60)
166
+ print("ExCom AI - Simple Tool Calling Chat")
167
+ print("=" * 60)
168
+ print("Available tools:")
169
+ print(" • calculator - Evaluate math expressions")
170
+ print(" • get_current_time - Get current date/time")
171
+ print(" • get_weather - Get weather for cities")
172
+ print("\nType 'quit' or 'exit' to end.")
173
+ print("=" * 60)
174
+ print()
175
+
176
+ messages = []
177
+
178
+ while True:
179
+ user_input = input("You: ").strip()
180
+
181
+ if user_input.lower() in ['quit', 'exit', 'q']:
182
+ print("Goodbye!")
183
+ break
184
+
185
+ if not user_input:
186
+ continue
187
+
188
+ try:
189
+ response, messages = chat(user_input, messages)
190
+ print(f"Assistant: {response}\n")
191
+ except Exception as e:
192
+ print(f"❌ Error: {e}\n")
193
+ # Reset messages on error
194
+ messages = []
195
+
196
+ if __name__ == "__main__":
197
+ main()