|
29 | 29 | router = APIRouter(tags=["tools"]) |
30 | 30 |
|
31 | 31 |
|
| 32 | +def _input_schema_to_parameters( |
| 33 | + schema: dict[str, Any] | None, |
| 34 | +) -> list[dict[str, Any]]: |
| 35 | + """Convert a JSON Schema input_schema to a flat list of parameter dicts. |
| 36 | +
|
| 37 | + The Llama Stack SDK returns tool parameters as a JSON Schema object |
| 38 | + (``input_schema``). This function converts that representation into |
| 39 | + the flat parameter list format used by the tools endpoint response. |
| 40 | +
|
| 41 | + Parameters: |
| 42 | + schema: JSON Schema dict with ``properties`` and ``required`` keys, |
| 43 | + or ``None`` if the tool has no parameters. |
| 44 | +
|
| 45 | + Returns: |
| 46 | + A list of parameter dicts, each containing ``name``, ``description``, |
| 47 | + ``parameter_type``, ``required``, and ``default`` keys. |
| 48 | + """ |
| 49 | + if not schema or "properties" not in schema: |
| 50 | + return [] |
| 51 | + |
| 52 | + required_params = set(schema.get("required", [])) |
| 53 | + return [ |
| 54 | + { |
| 55 | + "name": name, |
| 56 | + "description": prop.get("description", ""), |
| 57 | + "parameter_type": prop.get("type", "string"), |
| 58 | + "required": name in required_params, |
| 59 | + "default": prop.get("default"), |
| 60 | + } |
| 61 | + for name, prop in schema["properties"].items() |
| 62 | + ] |
| 63 | + |
| 64 | + |
| 65 | +def _normalize_tool_dict(tool_dict: dict[str, Any], toolgroup: Any) -> None: |
| 66 | + """Normalize a ToolDef dict to the endpoint's response format. |
| 67 | +
|
| 68 | + Remaps field names (``name`` -> ``identifier``, ``input_schema`` -> |
| 69 | + ``parameters``) and propagates ``provider_id``/``type`` from the |
| 70 | + parent toolgroup. Handles both missing keys and empty legacy |
| 71 | + placeholders. |
| 72 | + """ |
| 73 | + if "name" in tool_dict and not tool_dict.get("identifier"): |
| 74 | + tool_dict["identifier"] = tool_dict["name"] |
| 75 | + tool_dict.pop("name", None) |
| 76 | + |
| 77 | + if "input_schema" in tool_dict and not tool_dict.get("parameters"): |
| 78 | + tool_dict["parameters"] = _input_schema_to_parameters(tool_dict["input_schema"]) |
| 79 | + tool_dict.pop("input_schema", None) |
| 80 | + |
| 81 | + if not tool_dict.get("provider_id"): |
| 82 | + tool_dict["provider_id"] = toolgroup.provider_id |
| 83 | + if not tool_dict.get("type"): |
| 84 | + tool_dict["type"] = getattr(toolgroup, "type", None) or "tool" |
| 85 | + |
| 86 | + |
32 | 87 | tools_responses: dict[int | str, dict[str, Any]] = { |
33 | 88 | 200: ToolsResponse.openapi_response(), |
34 | 89 | 401: UnauthorizedResponse.openapi_response( |
@@ -120,6 +175,8 @@ async def tools_endpoint_handler( # pylint: disable=too-many-locals,too-many-st |
120 | 175 | for tool in tools_response: |
121 | 176 | tool_dict = dict(tool) |
122 | 177 |
|
| 178 | + _normalize_tool_dict(tool_dict, toolgroup) |
| 179 | + |
123 | 180 | # Determine server source based on toolgroup type |
124 | 181 | if toolgroup.identifier in mcp_server_names: |
125 | 182 | # This is an MCP server toolgroup |
|
0 commit comments