diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py index 8c2ad52fc..ad520ed09 100644 --- a/backend/open_webui/utils/tools.py +++ b/backend/open_webui/utils/tools.py @@ -374,60 +374,64 @@ def convert_openapi_to_tool_payload(openapi_spec): for path, methods in openapi_spec.get("paths", {}).items(): for method, operation in methods.items(): - tool = { - "type": "function", - "name": operation.get("operationId"), - "description": operation.get( - "description", operation.get("summary", "No description available.") - ), - "parameters": {"type": "object", "properties": {}, "required": []}, - } - - # Extract path and query parameters - for param in operation.get("parameters", []): - param_name = param["name"] - param_schema = param.get("schema", {}) - description = param_schema.get("description", "") - if not description: - description = param.get("description") or "" - if param_schema.get("enum") and isinstance( - param_schema.get("enum"), list - ): - description += ( - f". Possible values: {', '.join(param_schema.get('enum'))}" - ) - tool["parameters"]["properties"][param_name] = { - "type": param_schema.get("type"), - "description": description, + if operation.get("operationId"): + tool = { + "type": "function", + "name": operation.get("operationId"), + "description": operation.get( + "description", + operation.get("summary", "No description available."), + ), + "parameters": {"type": "object", "properties": {}, "required": []}, } - if param.get("required"): - tool["parameters"]["required"].append(param_name) - # Extract and resolve requestBody if available - request_body = operation.get("requestBody") - if request_body: - content = request_body.get("content", {}) - json_schema = content.get("application/json", {}).get("schema") - if json_schema: - resolved_schema = resolve_schema( - json_schema, openapi_spec.get("components", {}) - ) - - if resolved_schema.get("properties"): - tool["parameters"]["properties"].update( - resolved_schema["properties"] + # Extract path and query parameters + for param in operation.get("parameters", []): + param_name = param["name"] + param_schema = param.get("schema", {}) + description = param_schema.get("description", "") + if not description: + description = param.get("description") or "" + if param_schema.get("enum") and isinstance( + param_schema.get("enum"), list + ): + description += ( + f". Possible values: {', '.join(param_schema.get('enum'))}" ) - if "required" in resolved_schema: - tool["parameters"]["required"] = list( - set( - tool["parameters"]["required"] - + resolved_schema["required"] - ) - ) - elif resolved_schema.get("type") == "array": - tool["parameters"] = resolved_schema # special case for array + tool["parameters"]["properties"][param_name] = { + "type": param_schema.get("type"), + "description": description, + } + if param.get("required"): + tool["parameters"]["required"].append(param_name) - tool_payload.append(tool) + # Extract and resolve requestBody if available + request_body = operation.get("requestBody") + if request_body: + content = request_body.get("content", {}) + json_schema = content.get("application/json", {}).get("schema") + if json_schema: + resolved_schema = resolve_schema( + json_schema, openapi_spec.get("components", {}) + ) + + if resolved_schema.get("properties"): + tool["parameters"]["properties"].update( + resolved_schema["properties"] + ) + if "required" in resolved_schema: + tool["parameters"]["required"] = list( + set( + tool["parameters"]["required"] + + resolved_schema["required"] + ) + ) + elif resolved_schema.get("type") == "array": + tool["parameters"] = ( + resolved_schema # special case for array + ) + + tool_payload.append(tool) return tool_payload diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index e380f49ff..2dbca7759 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -1243,60 +1243,62 @@ export const convertOpenApiToToolPayload = (openApiSpec) => { for (const [path, methods] of Object.entries(openApiSpec.paths)) { for (const [method, operation] of Object.entries(methods)) { - const tool = { - type: 'function', - name: operation.operationId, - description: operation.description || operation.summary || 'No description available.', - parameters: { - type: 'object', - properties: {}, - required: [] - } - }; - - // Extract path and query parameters - if (operation.parameters) { - operation.parameters.forEach((param) => { - let description = param.schema.description || param.description || ''; - if (param.schema.enum && Array.isArray(param.schema.enum)) { - description += `. Possible values: ${param.schema.enum.join(', ')}`; + if (operation?.operationId) { + const tool = { + type: 'function', + name: operation.operationId, + description: operation.description || operation.summary || 'No description available.', + parameters: { + type: 'object', + properties: {}, + required: [] } - tool.parameters.properties[param.name] = { - type: param.schema.type, - description: description - }; + }; - if (param.required) { - tool.parameters.required.push(param.name); - } - }); - } - - // Extract and recursively resolve requestBody if available - if (operation.requestBody) { - const content = operation.requestBody.content; - if (content && content['application/json']) { - const requestSchema = content['application/json'].schema; - const resolvedRequestSchema = resolveSchema(requestSchema, openApiSpec.components); - - if (resolvedRequestSchema.properties) { - tool.parameters.properties = { - ...tool.parameters.properties, - ...resolvedRequestSchema.properties + // Extract path and query parameters + if (operation.parameters) { + operation.parameters.forEach((param) => { + let description = param.schema.description || param.description || ''; + if (param.schema.enum && Array.isArray(param.schema.enum)) { + description += `. Possible values: ${param.schema.enum.join(', ')}`; + } + tool.parameters.properties[param.name] = { + type: param.schema.type, + description: description }; - if (resolvedRequestSchema.required) { - tool.parameters.required = [ - ...new Set([...tool.parameters.required, ...resolvedRequestSchema.required]) - ]; + if (param.required) { + tool.parameters.required.push(param.name); + } + }); + } + + // Extract and recursively resolve requestBody if available + if (operation.requestBody) { + const content = operation.requestBody.content; + if (content && content['application/json']) { + const requestSchema = content['application/json'].schema; + const resolvedRequestSchema = resolveSchema(requestSchema, openApiSpec.components); + + if (resolvedRequestSchema.properties) { + tool.parameters.properties = { + ...tool.parameters.properties, + ...resolvedRequestSchema.properties + }; + + if (resolvedRequestSchema.required) { + tool.parameters.required = [ + ...new Set([...tool.parameters.required, ...resolvedRequestSchema.required]) + ]; + } + } else if (resolvedRequestSchema.type === 'array') { + tool.parameters = resolvedRequestSchema; // special case when root schema is an array } - } else if (resolvedRequestSchema.type === 'array') { - tool.parameters = resolvedRequestSchema; // special case when root schema is an array } } - } - toolPayload.push(tool); + toolPayload.push(tool); + } } } @@ -1304,15 +1306,17 @@ export const convertOpenApiToToolPayload = (openApiSpec) => { }; export const slugify = (str: string): string => { - return str - // 1. Normalize: separate accented letters into base + combining marks - .normalize("NFD") - // 2. Remove all combining marks (the accents) - .replace(/[\u0300-\u036f]/g, "") - // 3. Replace any sequence of whitespace with a single hyphen - .replace(/\s+/g, "-") - // 4. Remove all characters except alphanumeric characters and hyphens - .replace(/[^a-zA-Z0-9-]/g, "") - // 5. Convert to lowercase - .toLowerCase(); + return ( + str + // 1. Normalize: separate accented letters into base + combining marks + .normalize('NFD') + // 2. Remove all combining marks (the accents) + .replace(/[\u0300-\u036f]/g, '') + // 3. Replace any sequence of whitespace with a single hyphen + .replace(/\s+/g, '-') + // 4. Remove all characters except alphanumeric characters and hyphens + .replace(/[^a-zA-Z0-9-]/g, '') + // 5. Convert to lowercase + .toLowerCase() + ); };