From 20c1dc5a82340606067e7b23c0e0e758e2407c09 Mon Sep 17 00:00:00 2001 From: rajkstats Date: Tue, 11 Feb 2025 02:42:22 +0530 Subject: [PATCH 1/4] Enhance Parsing of Unparsed Function Calls in LLM Output --- os_computer_use/llm_provider.py | 38 ++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/os_computer_use/llm_provider.py b/os_computer_use/llm_provider.py index 4482249..78de4d1 100644 --- a/os_computer_use/llm_provider.py +++ b/os_computer_use/llm_provider.py @@ -22,6 +22,29 @@ def parse_json(s): return None +def extract_json_objects(s): + """Extract all balanced JSON objects from a string.""" + objects = [] + brace_level = 0 + start_index = None + for i, char in enumerate(s): + if char == "{": + if brace_level == 0: + start_index = i + brace_level += 1 + elif char == "}": + brace_level -= 1 + if brace_level == 0 and start_index is not None: + candidate = s[start_index : i + 1] + try: + obj = json.loads(candidate) + objects.append(obj) + except json.JSONDecodeError: + pass + start_index = None + return objects + + class LLMProvider: """ The LLM provider is used to make calls to an LLM given a provider and model name, with optional tool use support @@ -142,16 +165,15 @@ def call(self, messages, functions=None): # Sometimes, function calls are returned unparsed by the inference provider. This code parses them manually. if message.content and not tool_calls: - tool_call_matches = re.search(r"\{.*\}", message.content) - if tool_call_matches: - tool_call = parse_json(tool_call_matches.group(0)) - # Some models use "arguments" as the key instead of "parameters" - parameters = tool_call.get("parameters", tool_call.get("arguments")) - if tool_call.get("name") and parameters: + json_objs = extract_json_objects(message.content) + for obj in json_objs: + parameters = obj.get("parameters", obj.get("arguments")) + if obj.get("name") and parameters is not None: combined_tool_calls.append( - self.create_tool_call(tool_call.get("name"), parameters) + self.create_tool_call(obj.get("name"), parameters) ) - return None, combined_tool_calls + if combined_tool_calls: + return None, combined_tool_calls return message.content, combined_tool_calls From 660dbd7f267f671a546fcef87429f2978a488f3e Mon Sep 17 00:00:00 2001 From: rajkstats Date: Tue, 11 Feb 2025 02:56:01 +0530 Subject: [PATCH 2/4] dummy property for gemini --- os_computer_use/llm_provider.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/os_computer_use/llm_provider.py b/os_computer_use/llm_provider.py index 78de4d1..a937a24 100644 --- a/os_computer_use/llm_provider.py +++ b/os_computer_use/llm_provider.py @@ -75,6 +75,13 @@ def create_function_schema(self, definitions): properties[param_name] = {"type": "string", "description": param_desc} required.append(param_name) + # Add a dummy property if no parameters are provided, because providers like Gemini require a non-empty "properties" object. + if not properties: + properties["noop"] = { + "type": "string", + "description": "Dummy parameter for function with no parameters.", + } + function_def = self.create_function_def(name, details, properties, required) functions.append(function_def) From 2df9fddeb33d326c8b2f81be44295432da5df7af Mon Sep 17 00:00:00 2001 From: rajkstats Date: Wed, 12 Feb 2025 14:04:34 +0530 Subject: [PATCH 3/4] Keeping only relevant changes --- os_computer_use/llm_provider.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/os_computer_use/llm_provider.py b/os_computer_use/llm_provider.py index a937a24..78de4d1 100644 --- a/os_computer_use/llm_provider.py +++ b/os_computer_use/llm_provider.py @@ -75,13 +75,6 @@ def create_function_schema(self, definitions): properties[param_name] = {"type": "string", "description": param_desc} required.append(param_name) - # Add a dummy property if no parameters are provided, because providers like Gemini require a non-empty "properties" object. - if not properties: - properties["noop"] = { - "type": "string", - "description": "Dummy parameter for function with no parameters.", - } - function_def = self.create_function_def(name, details, properties, required) functions.append(function_def) From 436fb2ddd6674f94c90d2f837a30df2582116218 Mon Sep 17 00:00:00 2001 From: rajkstats Date: Sun, 2 Mar 2025 19:14:07 +0530 Subject: [PATCH 4/4] Changes wrt gemini models --- os_computer_use/llm_provider.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/os_computer_use/llm_provider.py b/os_computer_use/llm_provider.py index 78de4d1..a937a24 100644 --- a/os_computer_use/llm_provider.py +++ b/os_computer_use/llm_provider.py @@ -75,6 +75,13 @@ def create_function_schema(self, definitions): properties[param_name] = {"type": "string", "description": param_desc} required.append(param_name) + # Add a dummy property if no parameters are provided, because providers like Gemini require a non-empty "properties" object. + if not properties: + properties["noop"] = { + "type": "string", + "description": "Dummy parameter for function with no parameters.", + } + function_def = self.create_function_def(name, details, properties, required) functions.append(function_def)