Skip to content

Commit 0ec746d

Browse files
Merge pull request #510 from evison/main
fix: Update AIOS core components to fix the issue of tool calling under weak LLMs
2 parents d34f630 + 3b2a633 commit 0ec746d

File tree

4 files changed

+61
-20
lines changed

4 files changed

+61
-20
lines changed

aios/config/config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ llms:
4444
- name: "qwen3:1.7b"
4545
backend: "ollama"
4646
hostname: "http://localhost:11434" # Make sure to run ollama server
47-
47+
4848
- name: "qwen3:4b"
4949
backend: "ollama"
5050
hostname: "http://localhost:11434" # Make sure to run ollama server

aios/llm_core/utils.py

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
import re
33
import uuid
44
from copy import deepcopy
5-
5+
import logging
66
from typing import List, Dict, Any
77

8+
# Configure logging
9+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
10+
logger = logging.getLogger(__name__)
11+
812
def merge_messages_with_tools(messages: list, tools: list) -> list:
913
"""
1014
Integrate tool information into the messages for open-sourced LLMs which don't support tool calling.
@@ -193,8 +197,8 @@ def decode_litellm_tool_calls(response):
193197
```
194198
"""
195199
decoded_tool_calls = []
196-
197-
if response.choices[0].message.content is None:
200+
201+
if response.choices[0].message.content is None:
198202
assert response.choices[0].message.tool_calls is not None
199203
tool_calls = response.choices[0].message.tool_calls
200204

@@ -211,24 +215,53 @@ def decode_litellm_tool_calls(response):
211215
)
212216
else:
213217
assert response.choices[0].message.content is not None
214-
215-
# breakpoint()
218+
219+
# Some providers return a JSON string; attempt to parse. If parsing fails, treat as "no tools".
216220
tool_calls = response.choices[0].message.content
217221
if isinstance(tool_calls, str):
218-
tool_calls = json.loads(tool_calls)
219-
222+
try:
223+
parsed = json.loads(tool_calls)
224+
if isinstance(parsed, (list, dict)):
225+
tool_calls = parsed
226+
# Unexpected JSON type → no-op; be forgiving.
227+
else:
228+
logger.info("decode_litellm_tool_calls: unexpected JSON type for tool_calls: %s", type(parsed))
229+
tool_calls = []
230+
except json.JSONDecodeError:
231+
logger.info("decode_litellm_tool_calls: non-JSON tool_calls string, treating as no tools.")
232+
tool_calls = []
233+
220234
if not isinstance(tool_calls, list):
221235
tool_calls = [tool_calls]
222-
223-
for tool_call in tool_calls:
224-
decoded_tool_calls.append(
225-
{
226-
"name": tool_call["name"],
227-
"parameters": tool_call["arguments"],
228-
"id": generator_tool_call_id()
229-
}
230-
)
231236

237+
try:
238+
for tool_call in tool_calls:
239+
name = None
240+
if "name" in tool_call:
241+
name = tool_call["name"]
242+
elif "function_name" in tool_call:
243+
name = tool_call["function_name"]
244+
elif "tool_name" in tool_call:
245+
name = tool_call["tool_name"]
246+
247+
if name is not None:
248+
parameters = None
249+
if "arguments" in tool_call:
250+
parameters = tool_call["arguments"]
251+
elif "parameters" in tool_call:
252+
parameters = tool_call["parameters"]
253+
254+
if parameters is not None:
255+
decoded_tool_calls.append(
256+
{
257+
"name": name,
258+
"parameters": parameters,
259+
"id": generator_tool_call_id()
260+
}
261+
)
262+
except:
263+
logger.info(f"decode_litellm_tool_calls: no valid attribute in tools, treating as no tools")
264+
232265
return decoded_tool_calls
233266

234267
def parse_tool_calls(message):

aios/syscall/syscall.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -654,13 +654,16 @@ def execute_request(self, agent_name: str, query: Any) -> Dict[str, Any]:
654654

655655
elif query.action_type == "call_tool":
656656
llm_response = self.execute_llm_syscall(agent_name, query)["response"]
657-
# breakpoint()
657+
if llm_response.tool_calls == None or len(llm_response.tool_calls) == 0:
658+
return ToolResponse(
659+
response_message=f"No tool was called by LLM",
660+
finished=False
661+
)
658662
tool_query = ToolQuery(
659663
tool_calls=llm_response.tool_calls,
660664
# action_type="tool_use"
661665
)
662666
tool_response = self.execute_tool_syscall(agent_name, tool_query)
663-
# breakpoint()
664667
return tool_response
665668

666669
elif query.action_type == "operate_file":

aios/tool/manager.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ def cleanup(self):
8080
def address_request(self, syscall) -> None:
8181
tool_calls = syscall.query.tool_calls
8282

83+
if tool_calls == None or len(tool_calls) == 0:
84+
return ToolResponse(
85+
response_message=f"There is no tool to call",
86+
finished=False
87+
)
8388
# breakpoint()
8489
try:
8590
for tool_call in tool_calls:
@@ -106,7 +111,7 @@ def address_request(self, syscall) -> None:
106111
except Exception as e:
107112
return ToolResponse(
108113
response_message=f"Tool calling error: {e}",
109-
finished=True
114+
finished=False
110115
)
111116

112117
def load_tool_instance(self, tool_org_and_name):

0 commit comments

Comments
 (0)