diff --git a/docs/_static/llama-stack-spec.html b/docs/_static/llama-stack-spec.html
index 1d1b14b4a..567110829 100644
--- a/docs/_static/llama-stack-spec.html
+++ b/docs/_static/llama-stack-spec.html
@@ -2688,9 +2688,9 @@
"200": {
"description": "OK",
"content": {
- "application/jsonl": {
+ "application/json": {
"schema": {
- "$ref": "#/components/schemas/ToolDef"
+ "$ref": "#/components/schemas/ListToolDefsResponse"
}
}
}
@@ -8328,6 +8328,22 @@
],
"title": "ListRoutesResponse"
},
+ "ListToolDefsResponse": {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ToolDef"
+ }
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "data"
+ ],
+ "title": "ListToolDefsResponse"
+ },
"ListScoringFunctionsResponse": {
"type": "object",
"properties": {
diff --git a/docs/_static/llama-stack-spec.yaml b/docs/_static/llama-stack-spec.yaml
index c98e1de89..1dfd17f55 100644
--- a/docs/_static/llama-stack-spec.yaml
+++ b/docs/_static/llama-stack-spec.yaml
@@ -1855,9 +1855,9 @@ paths:
'200':
description: OK
content:
- application/jsonl:
+ application/json:
schema:
- $ref: '#/components/schemas/ToolDef'
+ $ref: '#/components/schemas/ListToolDefsResponse'
'400':
$ref: '#/components/responses/BadRequest400'
'429':
@@ -5732,6 +5732,17 @@ components:
required:
- data
title: ListRoutesResponse
+ ListToolDefsResponse:
+ type: object
+ properties:
+ data:
+ type: array
+ items:
+ $ref: '#/components/schemas/ToolDef'
+ additionalProperties: false
+ required:
+ - data
+ title: ListToolDefsResponse
ListScoringFunctionsResponse:
type: object
properties:
diff --git a/docs/openapi_generator/pyopenapi/utility.py b/docs/openapi_generator/pyopenapi/utility.py
index 47b72001b..db18e8430 100644
--- a/docs/openapi_generator/pyopenapi/utility.py
+++ b/docs/openapi_generator/pyopenapi/utility.py
@@ -132,7 +132,18 @@ def _validate_api_method_return_type(method) -> str | None:
return_type = hints['return']
if is_optional_type(return_type):
- return "returns Optional type"
+ return "returns Optional type where a return value is mandatory"
+
+
+def _validate_api_method_doesnt_return_list(method) -> str | None:
+ hints = get_type_hints(method)
+
+ if 'return' not in hints:
+ return "has no return type annotation"
+
+ return_type = hints['return']
+ if get_origin(return_type) is list:
+ return "returns a list where a PaginatedResponse or List*Response object is expected"
def _validate_api_delete_method_returns_none(method) -> str | None:
@@ -143,7 +154,7 @@ def _validate_api_delete_method_returns_none(method) -> str | None:
return_type = hints['return']
if return_type is not None and return_type is not type(None):
- return "does not return None"
+ return "does not return None where None is mandatory"
def _validate_list_parameters_contain_data(method) -> str | None:
@@ -160,13 +171,14 @@ def _validate_list_parameters_contain_data(method) -> str | None:
return
if 'data' not in return_type.model_fields:
- return "does not have data attribute"
+ return "does not have a mandatory data attribute containing the list of objects"
_VALIDATORS = {
"GET": [
_validate_api_method_return_type,
_validate_list_parameters_contain_data,
+ _validate_api_method_doesnt_return_list,
],
"DELETE": [
_validate_api_delete_method_returns_none,
diff --git a/llama_stack/apis/tools/tools.py b/llama_stack/apis/tools/tools.py
index e0744a75e..4ca72f71d 100644
--- a/llama_stack/apis/tools/tools.py
+++ b/llama_stack/apis/tools/tools.py
@@ -88,6 +88,10 @@ class ListToolsResponse(BaseModel):
data: List[Tool]
+class ListToolDefsResponse(BaseModel):
+ data: list[ToolDef]
+
+
@runtime_checkable
@trace_protocol
class ToolGroups(Protocol):
@@ -148,7 +152,7 @@ class ToolRuntime(Protocol):
@webmethod(route="/tool-runtime/list-tools", method="GET")
async def list_runtime_tools(
self, tool_group_id: Optional[str] = None, mcp_endpoint: Optional[URL] = None
- ) -> List[ToolDef]: ...
+ ) -> ListToolDefsResponse: ...
@webmethod(route="/tool-runtime/invoke", method="POST")
async def invoke_tool(self, tool_name: str, kwargs: Dict[str, Any]) -> ToolInvocationResult:
diff --git a/llama_stack/distribution/routers/routers.py b/llama_stack/distribution/routers/routers.py
index 53f21f9d8..eed96a40a 100644
--- a/llama_stack/distribution/routers/routers.py
+++ b/llama_stack/distribution/routers/routers.py
@@ -46,11 +46,11 @@ from llama_stack.apis.scoring import (
from llama_stack.apis.shields import Shield
from llama_stack.apis.telemetry import MetricEvent, MetricInResponse, Telemetry
from llama_stack.apis.tools import (
+ ListToolDefsResponse,
RAGDocument,
RAGQueryConfig,
RAGQueryResult,
RAGToolRuntime,
- ToolDef,
ToolRuntime,
)
from llama_stack.apis.vector_io import Chunk, QueryChunksResponse, VectorIO
@@ -707,6 +707,6 @@ class ToolRuntimeRouter(ToolRuntime):
async def list_runtime_tools(
self, tool_group_id: Optional[str] = None, mcp_endpoint: Optional[URL] = None
- ) -> List[ToolDef]:
+ ) -> ListToolDefsResponse:
logger.debug(f"ToolRuntimeRouter.list_runtime_tools: {tool_group_id}")
return await self.routing_table.get_provider_impl(tool_group_id).list_tools(tool_group_id, mcp_endpoint)
diff --git a/llama_stack/distribution/routers/routing_tables.py b/llama_stack/distribution/routers/routing_tables.py
index d444b03a3..557330df7 100644
--- a/llama_stack/distribution/routers/routing_tables.py
+++ b/llama_stack/distribution/routers/routing_tables.py
@@ -568,7 +568,7 @@ class ToolGroupsRoutingTable(CommonRoutingTableImpl, ToolGroups):
tool_defs = await self.impls_by_provider_id[provider_id].list_runtime_tools(toolgroup_id, mcp_endpoint)
tool_host = ToolHost.model_context_protocol if mcp_endpoint else ToolHost.distribution
- for tool_def in tool_defs:
+ for tool_def in tool_defs.data:
tools.append(
ToolWithACL(
identifier=tool_def.name,
diff --git a/llama_stack/providers/inline/tool_runtime/code_interpreter/code_interpreter.py b/llama_stack/providers/inline/tool_runtime/code_interpreter/code_interpreter.py
index 9610b9b46..10ac2fcc6 100644
--- a/llama_stack/providers/inline/tool_runtime/code_interpreter/code_interpreter.py
+++ b/llama_stack/providers/inline/tool_runtime/code_interpreter/code_interpreter.py
@@ -9,10 +9,11 @@ import asyncio
import logging
import os
import tempfile
-from typing import Any, Dict, List, Optional
+from typing import Any, Dict, Optional
from llama_stack.apis.common.content_types import URL
from llama_stack.apis.tools import (
+ ListToolDefsResponse,
Tool,
ToolDef,
ToolInvocationResult,
@@ -46,20 +47,22 @@ class CodeInterpreterToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime):
async def list_runtime_tools(
self, tool_group_id: Optional[str] = None, mcp_endpoint: Optional[URL] = None
- ) -> List[ToolDef]:
- return [
- ToolDef(
- name="code_interpreter",
- description="Execute code",
- parameters=[
- ToolParameter(
- name="code",
- description="The code to execute",
- parameter_type="string",
- ),
- ],
- )
- ]
+ ) -> ListToolDefsResponse:
+ return ListToolDefsResponse(
+ data=[
+ ToolDef(
+ name="code_interpreter",
+ description="Execute code",
+ parameters=[
+ ToolParameter(
+ name="code",
+ description="The code to execute",
+ parameter_type="string",
+ ),
+ ],
+ )
+ ]
+ )
async def invoke_tool(self, tool_name: str, kwargs: Dict[str, Any]) -> ToolInvocationResult:
script = kwargs["code"]
diff --git a/llama_stack/providers/inline/tool_runtime/rag/memory.py b/llama_stack/providers/inline/tool_runtime/rag/memory.py
index 8dd846c6f..97c53d454 100644
--- a/llama_stack/providers/inline/tool_runtime/rag/memory.py
+++ b/llama_stack/providers/inline/tool_runtime/rag/memory.py
@@ -20,6 +20,7 @@ from llama_stack.apis.common.content_types import (
)
from llama_stack.apis.inference import Inference
from llama_stack.apis.tools import (
+ ListToolDefsResponse,
RAGDocument,
RAGQueryConfig,
RAGQueryResult,
@@ -162,27 +163,29 @@ class MemoryToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime, RAGToolRuntime):
async def list_runtime_tools(
self, tool_group_id: Optional[str] = None, mcp_endpoint: Optional[URL] = None
- ) -> List[ToolDef]:
+ ) -> ListToolDefsResponse:
# Parameters are not listed since these methods are not yet invoked automatically
# by the LLM. The method is only implemented so things like /tools can list without
# encountering fatals.
- return [
- ToolDef(
- name="insert_into_memory",
- description="Insert documents into memory",
- ),
- ToolDef(
- name="knowledge_search",
- description="Search for information in a database.",
- parameters=[
- ToolParameter(
- name="query",
- description="The query to search for. Can be a natural language sentence or keywords.",
- parameter_type="string",
- ),
- ],
- ),
- ]
+ return ListToolDefsResponse(
+ data=[
+ ToolDef(
+ name="insert_into_memory",
+ description="Insert documents into memory",
+ ),
+ ToolDef(
+ name="knowledge_search",
+ description="Search for information in a database.",
+ parameters=[
+ ToolParameter(
+ name="query",
+ description="The query to search for. Can be a natural language sentence or keywords.",
+ parameter_type="string",
+ ),
+ ],
+ ),
+ ]
+ )
async def invoke_tool(self, tool_name: str, kwargs: Dict[str, Any]) -> ToolInvocationResult:
vector_db_ids = kwargs.get("vector_db_ids", [])
diff --git a/llama_stack/providers/remote/tool_runtime/bing_search/bing_search.py b/llama_stack/providers/remote/tool_runtime/bing_search/bing_search.py
index f494a7fbb..b34c9fd9d 100644
--- a/llama_stack/providers/remote/tool_runtime/bing_search/bing_search.py
+++ b/llama_stack/providers/remote/tool_runtime/bing_search/bing_search.py
@@ -5,12 +5,13 @@
# the root directory of this source tree.
import json
-from typing import Any, Dict, List, Optional
+from typing import Any, Dict, Optional
import httpx
from llama_stack.apis.common.content_types import URL
from llama_stack.apis.tools import (
+ ListToolDefsResponse,
Tool,
ToolDef,
ToolInvocationResult,
@@ -50,20 +51,22 @@ class BingSearchToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime, NeedsRequestP
async def list_runtime_tools(
self, tool_group_id: Optional[str] = None, mcp_endpoint: Optional[URL] = None
- ) -> List[ToolDef]:
- return [
- ToolDef(
- name="web_search",
- description="Search the web using Bing Search API",
- parameters=[
- ToolParameter(
- name="query",
- description="The query to search for",
- parameter_type="string",
- )
- ],
- )
- ]
+ ) -> ListToolDefsResponse:
+ return ListToolDefsResponse(
+ data=[
+ ToolDef(
+ name="web_search",
+ description="Search the web using Bing Search API",
+ parameters=[
+ ToolParameter(
+ name="query",
+ description="The query to search for",
+ parameter_type="string",
+ )
+ ],
+ )
+ ]
+ )
async def invoke_tool(self, tool_name: str, kwargs: Dict[str, Any]) -> ToolInvocationResult:
api_key = self._get_api_key()
diff --git a/llama_stack/providers/remote/tool_runtime/brave_search/brave_search.py b/llama_stack/providers/remote/tool_runtime/brave_search/brave_search.py
index 78b47eb56..41f3ce823 100644
--- a/llama_stack/providers/remote/tool_runtime/brave_search/brave_search.py
+++ b/llama_stack/providers/remote/tool_runtime/brave_search/brave_search.py
@@ -4,12 +4,13 @@
# This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree.
-from typing import Any, Dict, List, Optional
+from typing import Any, Dict, Optional
import httpx
from llama_stack.apis.common.content_types import URL
from llama_stack.apis.tools import (
+ ListToolDefsResponse,
Tool,
ToolDef,
ToolInvocationResult,
@@ -49,21 +50,23 @@ class BraveSearchToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime, NeedsRequest
async def list_runtime_tools(
self, tool_group_id: Optional[str] = None, mcp_endpoint: Optional[URL] = None
- ) -> List[ToolDef]:
- return [
- ToolDef(
- name="web_search",
- description="Search the web for information",
- parameters=[
- ToolParameter(
- name="query",
- description="The query to search for",
- parameter_type="string",
- )
- ],
- built_in_type=BuiltinTool.brave_search,
- )
- ]
+ ) -> ListToolDefsResponse:
+ return ListToolDefsResponse(
+ data=[
+ ToolDef(
+ name="web_search",
+ description="Search the web for information",
+ parameters=[
+ ToolParameter(
+ name="query",
+ description="The query to search for",
+ parameter_type="string",
+ )
+ ],
+ built_in_type=BuiltinTool.brave_search,
+ )
+ ]
+ )
async def invoke_tool(self, tool_name: str, kwargs: Dict[str, Any]) -> ToolInvocationResult:
api_key = self._get_api_key()
diff --git a/llama_stack/providers/remote/tool_runtime/model_context_protocol/model_context_protocol.py b/llama_stack/providers/remote/tool_runtime/model_context_protocol/model_context_protocol.py
index f7dc376f8..676917225 100644
--- a/llama_stack/providers/remote/tool_runtime/model_context_protocol/model_context_protocol.py
+++ b/llama_stack/providers/remote/tool_runtime/model_context_protocol/model_context_protocol.py
@@ -4,7 +4,7 @@
# This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree.
-from typing import Any, Dict, List, Optional
+from typing import Any, Dict, Optional
from urllib.parse import urlparse
from mcp import ClientSession
@@ -12,6 +12,7 @@ from mcp.client.sse import sse_client
from llama_stack.apis.common.content_types import URL
from llama_stack.apis.tools import (
+ ListToolDefsResponse,
ToolDef,
ToolInvocationResult,
ToolParameter,
@@ -31,7 +32,7 @@ class ModelContextProtocolToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime):
async def list_runtime_tools(
self, tool_group_id: Optional[str] = None, mcp_endpoint: Optional[URL] = None
- ) -> List[ToolDef]:
+ ) -> ListToolDefsResponse:
if mcp_endpoint is None:
raise ValueError("mcp_endpoint is required")
@@ -60,7 +61,7 @@ class ModelContextProtocolToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime):
},
)
)
- return tools
+ return ListToolDefsResponse(data=tools)
async def invoke_tool(self, tool_name: str, kwargs: Dict[str, Any]) -> ToolInvocationResult:
tool = await self.tool_store.get_tool(tool_name)
diff --git a/llama_stack/providers/remote/tool_runtime/tavily_search/tavily_search.py b/llama_stack/providers/remote/tool_runtime/tavily_search/tavily_search.py
index 5b23d94d3..719d6be14 100644
--- a/llama_stack/providers/remote/tool_runtime/tavily_search/tavily_search.py
+++ b/llama_stack/providers/remote/tool_runtime/tavily_search/tavily_search.py
@@ -5,12 +5,13 @@
# the root directory of this source tree.
import json
-from typing import Any, Dict, List, Optional
+from typing import Any, Dict, Optional
import httpx
from llama_stack.apis.common.content_types import URL
from llama_stack.apis.tools import (
+ ListToolDefsResponse,
Tool,
ToolDef,
ToolInvocationResult,
@@ -49,20 +50,22 @@ class TavilySearchToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime, NeedsReques
async def list_runtime_tools(
self, tool_group_id: Optional[str] = None, mcp_endpoint: Optional[URL] = None
- ) -> List[ToolDef]:
- return [
- ToolDef(
- name="web_search",
- description="Search the web for information",
- parameters=[
- ToolParameter(
- name="query",
- description="The query to search for",
- parameter_type="string",
- )
- ],
- )
- ]
+ ) -> ListToolDefsResponse:
+ return ListToolDefsResponse(
+ data=[
+ ToolDef(
+ name="web_search",
+ description="Search the web for information",
+ parameters=[
+ ToolParameter(
+ name="query",
+ description="The query to search for",
+ parameter_type="string",
+ )
+ ],
+ )
+ ]
+ )
async def invoke_tool(self, tool_name: str, kwargs: Dict[str, Any]) -> ToolInvocationResult:
api_key = self._get_api_key()
diff --git a/llama_stack/providers/remote/tool_runtime/wolfram_alpha/wolfram_alpha.py b/llama_stack/providers/remote/tool_runtime/wolfram_alpha/wolfram_alpha.py
index 8489fa7d8..b3e0e120c 100644
--- a/llama_stack/providers/remote/tool_runtime/wolfram_alpha/wolfram_alpha.py
+++ b/llama_stack/providers/remote/tool_runtime/wolfram_alpha/wolfram_alpha.py
@@ -5,12 +5,13 @@
# the root directory of this source tree.
import json
-from typing import Any, Dict, List, Optional
+from typing import Any, Dict, Optional
import httpx
from llama_stack.apis.common.content_types import URL
from llama_stack.apis.tools import (
+ ListToolDefsResponse,
Tool,
ToolDef,
ToolInvocationResult,
@@ -50,20 +51,22 @@ class WolframAlphaToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime, NeedsReques
async def list_runtime_tools(
self, tool_group_id: Optional[str] = None, mcp_endpoint: Optional[URL] = None
- ) -> List[ToolDef]:
- return [
- ToolDef(
- name="wolfram_alpha",
- description="Query WolframAlpha for computational knowledge",
- parameters=[
- ToolParameter(
- name="query",
- description="The query to compute",
- parameter_type="string",
- )
- ],
- )
- ]
+ ) -> ListToolDefsResponse:
+ return ListToolDefsResponse(
+ data=[
+ ToolDef(
+ name="wolfram_alpha",
+ description="Query WolframAlpha for computational knowledge",
+ parameters=[
+ ToolParameter(
+ name="query",
+ description="The query to compute",
+ parameter_type="string",
+ )
+ ],
+ )
+ ]
+ )
async def invoke_tool(self, tool_name: str, kwargs: Dict[str, Any]) -> ToolInvocationResult:
api_key = self._get_api_key()