refac: openapi to tool spec

This commit is contained in:
Timothy Jaeryang Baek 2025-04-19 03:46:06 -07:00
parent 332f8579d7
commit 463d7fb628
2 changed files with 115 additions and 107 deletions

View File

@ -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

View File

@ -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()
);
};