Make OpenAPI tool names provider-safe

This commit is contained in:
2026-06-28 14:18:37 -03:00
parent 5b97a01efc
commit d8559ca133

View File

@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import base64 import base64
import hashlib
import json import json
import re import re
from pathlib import Path from pathlib import Path
@@ -38,6 +39,34 @@ PATH_PARAMETER_RE = re.compile(r"{([^}/]+)}")
SOURCE_ROOT = Path(globals().get("__file__", "agent.py")).resolve().parent SOURCE_ROOT = Path(globals().get("__file__", "agent.py")).resolve().parent
SOURCE_SKILLS_DIR = SOURCE_ROOT / "skills" SOURCE_SKILLS_DIR = SOURCE_ROOT / "skills"
RUNTIME_SKILLS_DIR = ".deepagents/openapi-skills/" RUNTIME_SKILLS_DIR = ".deepagents/openapi-skills/"
MAX_OPENAI_TOOL_NAME_LENGTH = 64
def _openai_safe_tool_name(name: str, used: set[str]) -> str:
safe = re.sub(r"[^a-zA-Z0-9_-]+", "_", name).strip("_") or "operation"
if len(safe) > MAX_OPENAI_TOOL_NAME_LENGTH:
digest = hashlib.sha1(safe.encode("utf-8")).hexdigest()[:10]
prefix = safe[: MAX_OPENAI_TOOL_NAME_LENGTH - len(digest) - 1].rstrip("_-")
safe = f"{prefix or 'operation'}_{digest}"
candidate = safe
index = 2
while candidate in used:
suffix = f"_{index}"
candidate = safe[: MAX_OPENAI_TOOL_NAME_LENGTH - len(suffix)].rstrip("_-") + suffix
index += 1
used.add(candidate)
return candidate
def _normalize_operation_tool_names() -> None:
used: set[str] = set()
for operation_id, operation in OPERATIONS.items():
original = str(operation.get("skill_name") or operation_id)
operation["original_skill_name"] = original
operation["skill_name"] = _openai_safe_tool_name(original, used)
_normalize_operation_tool_names()
class OperationInput(BaseModel): class OperationInput(BaseModel):
@@ -241,6 +270,7 @@ class Learnhouse(A2AAgent):
) )
if operation.get("request_body") is not None: if operation.get("request_body") is not None:
parts.append("Accepts a JSON request body.") parts.append("Accepts a JSON request body.")
parts.append(f"Operation ID: {operation.get('operation_id') or operation.get('original_skill_name')}")
return "\n".join(part for part in parts if part) return "\n".join(part for part in parts if part)
async def _request( async def _request(
@@ -287,6 +317,7 @@ class Learnhouse(A2AAgent):
"ok": False, "ok": False,
"status_code": response.status_code, "status_code": response.status_code,
"operation_id": operation_id, "operation_id": operation_id,
"tool_name": operation.get("skill_name"),
"error": payload, "error": payload,
} }
if response.status_code in {404, 405, 410, 422}: if response.status_code in {404, 405, 410, 422}:
@@ -299,6 +330,7 @@ class Learnhouse(A2AAgent):
"ok": True, "ok": True,
"status_code": response.status_code, "status_code": response.status_code,
"operation_id": operation_id, "operation_id": operation_id,
"tool_name": operation.get("skill_name"),
"result": payload, "result": payload,
} }