Bladeren bron

fix: parameter type handling in API tool and parser (#2574)

Yeuoly 1 jaar geleden
bovenliggende
commit
ac96d192a6
2 gewijzigde bestanden met toevoegingen van 43 en 6 verwijderingen
  1. 1 1
      api/core/tools/tool/api_tool.py
  2. 42 5
      api/core/tools/utils/parser.py

+ 1 - 1
api/core/tools/tool/api_tool.py

@@ -200,7 +200,7 @@ class ApiTool(Tool):
         
         # replace path parameters
         for name, value in path_params.items():
-            url = url.replace(f'{{{name}}}', value)
+            url = url.replace(f'{{{name}}}', f'{value}')
 
         # parse http body data if needed, for GET/HEAD/OPTIONS/TRACE, the body is ignored
         if 'Content-Type' in headers:

+ 42 - 5
api/core/tools/utils/parser.py

@@ -1,4 +1,6 @@
 
+import re
+import uuid
 from json import loads as json_loads
 
 from requests import get
@@ -46,7 +48,7 @@ class ApiBasedToolSchemaParser:
             parameters = []
             if 'parameters' in interface['operation']:
                 for parameter in interface['operation']['parameters']:
-                    parameters.append(ToolParameter(
+                    tool_parameter = ToolParameter(
                         name=parameter['name'],
                         label=I18nObject(
                             en_US=parameter['name'],
@@ -61,7 +63,14 @@ class ApiBasedToolSchemaParser:
                         form=ToolParameter.ToolParameterForm.LLM,
                         llm_description=parameter.get('description'),
                         default=parameter['schema']['default'] if 'schema' in parameter and 'default' in parameter['schema'] else None,
-                    ))
+                    )
+                   
+                    # check if there is a type
+                    typ = ApiBasedToolSchemaParser._get_tool_parameter_type(parameter)
+                    if typ:
+                        tool_parameter.type = typ
+
+                    parameters.append(tool_parameter)
             # create tool bundle
             # check if there is a request body
             if 'requestBody' in interface['operation']:
@@ -80,13 +89,14 @@ class ApiBasedToolSchemaParser:
                                 root = root[ref]
                             # overwrite the content
                             interface['operation']['requestBody']['content'][content_type]['schema'] = root
+
                     # parse body parameters
                     if 'schema' in interface['operation']['requestBody']['content'][content_type]:
                         body_schema = interface['operation']['requestBody']['content'][content_type]['schema']
                         required = body_schema['required'] if 'required' in body_schema else []
                         properties = body_schema['properties'] if 'properties' in body_schema else {}
                         for name, property in properties.items():
-                            parameters.append(ToolParameter(
+                            tool = ToolParameter(
                                 name=name,
                                 label=I18nObject(
                                     en_US=name,
@@ -101,7 +111,14 @@ class ApiBasedToolSchemaParser:
                                 form=ToolParameter.ToolParameterForm.LLM,
                                 llm_description=property['description'] if 'description' in property else '',
                                 default=property['default'] if 'default' in property else None,
-                            ))
+                            )
+
+                            # check if there is a type
+                            typ = ApiBasedToolSchemaParser._get_tool_parameter_type(property)
+                            if typ:
+                                tool.type = typ
+
+                            parameters.append(tool)
 
             # check if parameters is duplicated
             parameters_count = {}
@@ -119,7 +136,11 @@ class ApiBasedToolSchemaParser:
                 path = interface['path']
                 if interface['path'].startswith('/'):
                     path = interface['path'][1:]
-                path = path.replace('/', '_')
+                # remove special characters like / to ensure the operation id is valid ^[a-zA-Z0-9_-]{1,64}$
+                path = re.sub(r'[^a-zA-Z0-9_-]', '', path)
+                if not path:
+                    path = str(uuid.uuid4())
+                    
                 interface['operation']['operationId'] = f'{path}_{interface["method"]}'
 
             bundles.append(ApiBasedToolBundle(
@@ -134,7 +155,23 @@ class ApiBasedToolSchemaParser:
             ))
 
         return bundles
+    
+    @staticmethod
+    def _get_tool_parameter_type(parameter: dict) -> ToolParameter.ToolParameterType:
+        parameter = parameter or {}
+        typ = None
+        if 'type' in parameter:
+            typ = parameter['type']
+        elif 'schema' in parameter and 'type' in parameter['schema']:
+            typ = parameter['schema']['type']
         
+        if typ == 'integer' or typ == 'number':
+            return ToolParameter.ToolParameterType.NUMBER
+        elif typ == 'boolean':
+            return ToolParameter.ToolParameterType.BOOLEAN
+        elif typ == 'string':
+            return ToolParameter.ToolParameterType.STRING
+
     @staticmethod
     def parse_openapi_yaml_to_tool_bundle(yaml: str, extra_info: dict = None, warning: dict = None) -> list[ApiBasedToolBundle]:
         """