diff --git a/docs/_static/llama-stack-spec.html b/docs/_static/llama-stack-spec.html
index 49c402d37..48f022aaa 100644
--- a/docs/_static/llama-stack-spec.html
+++ b/docs/_static/llama-stack-spec.html
@@ -6405,6 +6405,113 @@
"title": "OpenAIResponseInputMessageContentText"
},
"OpenAIResponseInputTool": {
+ "oneOf": [
+ {
+ "$ref": "#/components/schemas/OpenAIResponseInputToolWebSearch"
+ },
+ {
+ "$ref": "#/components/schemas/OpenAIResponseInputToolFileSearch"
+ },
+ {
+ "$ref": "#/components/schemas/OpenAIResponseInputToolFunction"
+ }
+ ],
+ "discriminator": {
+ "propertyName": "type",
+ "mapping": {
+ "web_search": "#/components/schemas/OpenAIResponseInputToolWebSearch",
+ "file_search": "#/components/schemas/OpenAIResponseInputToolFileSearch",
+ "function": "#/components/schemas/OpenAIResponseInputToolFunction"
+ }
+ }
+ },
+ "OpenAIResponseInputToolFileSearch": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "file_search",
+ "default": "file_search"
+ },
+ "vector_store_id": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "ranking_options": {
+ "type": "object",
+ "properties": {
+ "ranker": {
+ "type": "string"
+ },
+ "score_threshold": {
+ "type": "number",
+ "default": 0.0
+ }
+ },
+ "additionalProperties": false,
+ "title": "FileSearchRankingOptions"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "type",
+ "vector_store_id"
+ ],
+ "title": "OpenAIResponseInputToolFileSearch"
+ },
+ "OpenAIResponseInputToolFunction": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "const": "function",
+ "default": "function"
+ },
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "parameters": {
+ "type": "object",
+ "additionalProperties": {
+ "oneOf": [
+ {
+ "type": "null"
+ },
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "number"
+ },
+ {
+ "type": "string"
+ },
+ {
+ "type": "array"
+ },
+ {
+ "type": "object"
+ }
+ ]
+ }
+ },
+ "strict": {
+ "type": "boolean"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "type",
+ "name"
+ ],
+ "title": "OpenAIResponseInputToolFunction"
+ },
+ "OpenAIResponseInputToolWebSearch": {
"type": "object",
"properties": {
"type": {
diff --git a/docs/_static/llama-stack-spec.yaml b/docs/_static/llama-stack-spec.yaml
index e5bfad623..e7686b34f 100644
--- a/docs/_static/llama-stack-spec.yaml
+++ b/docs/_static/llama-stack-spec.yaml
@@ -4467,6 +4467,71 @@ components:
- type
title: OpenAIResponseInputMessageContentText
OpenAIResponseInputTool:
+ oneOf:
+ - $ref: '#/components/schemas/OpenAIResponseInputToolWebSearch'
+ - $ref: '#/components/schemas/OpenAIResponseInputToolFileSearch'
+ - $ref: '#/components/schemas/OpenAIResponseInputToolFunction'
+ discriminator:
+ propertyName: type
+ mapping:
+ web_search: '#/components/schemas/OpenAIResponseInputToolWebSearch'
+ file_search: '#/components/schemas/OpenAIResponseInputToolFileSearch'
+ function: '#/components/schemas/OpenAIResponseInputToolFunction'
+ OpenAIResponseInputToolFileSearch:
+ type: object
+ properties:
+ type:
+ type: string
+ const: file_search
+ default: file_search
+ vector_store_id:
+ type: array
+ items:
+ type: string
+ ranking_options:
+ type: object
+ properties:
+ ranker:
+ type: string
+ score_threshold:
+ type: number
+ default: 0.0
+ additionalProperties: false
+ title: FileSearchRankingOptions
+ additionalProperties: false
+ required:
+ - type
+ - vector_store_id
+ title: OpenAIResponseInputToolFileSearch
+ OpenAIResponseInputToolFunction:
+ type: object
+ properties:
+ type:
+ type: string
+ const: function
+ default: function
+ name:
+ type: string
+ description:
+ type: string
+ parameters:
+ type: object
+ additionalProperties:
+ oneOf:
+ - type: 'null'
+ - type: boolean
+ - type: number
+ - type: string
+ - type: array
+ - type: object
+ strict:
+ type: boolean
+ additionalProperties: false
+ required:
+ - type
+ - name
+ title: OpenAIResponseInputToolFunction
+ OpenAIResponseInputToolWebSearch:
type: object
properties:
type:
diff --git a/llama_stack/apis/agents/openai_responses.py b/llama_stack/apis/agents/openai_responses.py
index 72f16e224..71703cb5e 100644
--- a/llama_stack/apis/agents/openai_responses.py
+++ b/llama_stack/apis/agents/openai_responses.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 List, Literal, Optional, Union
+from typing import Any, Dict, List, Literal, Optional, Union
from pydantic import BaseModel, Field
from typing_extensions import Annotated
@@ -133,8 +133,30 @@ class OpenAIResponseInputToolWebSearch(BaseModel):
# TODO: add user_location
+@json_schema_type
+class OpenAIResponseInputToolFunction(BaseModel):
+ type: Literal["function"] = "function"
+ name: str
+ description: Optional[str] = None
+ parameters: Optional[Dict[str, Any]]
+ strict: Optional[bool]
+
+
+class FileSearchRankingOptions(BaseModel):
+ ranker: Optional[str] = None
+ score_threshold: Optional[float] = Field(default=0.0, ge=0.0, le=1.0)
+
+
+@json_schema_type
+class OpenAIResponseInputToolFileSearch(BaseModel):
+ type: Literal["file_search"] = "file_search"
+ vector_store_id: List[str]
+ ranking_options: Optional[FileSearchRankingOptions] = None
+ # TODO: add filters
+
+
OpenAIResponseInputTool = Annotated[
- Union[OpenAIResponseInputToolWebSearch,],
+ Union[OpenAIResponseInputToolWebSearch, OpenAIResponseInputToolFileSearch, OpenAIResponseInputToolFunction],
Field(discriminator="type"),
]
register_schema(OpenAIResponseInputTool, name="OpenAIResponseInputTool")
diff --git a/llama_stack/distribution/server/server.py b/llama_stack/distribution/server/server.py
index 0b21936c2..6ae3c7b8e 100644
--- a/llama_stack/distribution/server/server.py
+++ b/llama_stack/distribution/server/server.py
@@ -17,6 +17,7 @@ from importlib.metadata import version as parse_version
from pathlib import Path
from typing import Any, List, Optional, Union
+import rich.pretty
import yaml
from fastapi import Body, FastAPI, HTTPException, Request
from fastapi import Path as FastapiPath
@@ -187,11 +188,30 @@ async def sse_generator(event_gen_coroutine):
)
+async def log_request_pre_validation(request: Request):
+ if request.method in ("POST", "PUT", "PATCH"):
+ try:
+ body_bytes = await request.body()
+ if body_bytes:
+ try:
+ parsed_body = json.loads(body_bytes.decode())
+ log_output = rich.pretty.pretty_repr(parsed_body)
+ except (json.JSONDecodeError, UnicodeDecodeError):
+ log_output = repr(body_bytes)
+ logger.debug(f"Incoming raw request body for {request.method} {request.url.path}:\n{log_output}")
+ else:
+ logger.debug(f"Incoming {request.method} {request.url.path} request with empty body.")
+ except Exception as e:
+ logger.warning(f"Could not read or log request body for {request.method} {request.url.path}: {e}")
+
+
def create_dynamic_typed_route(func: Any, method: str, route: str):
async def endpoint(request: Request, **kwargs):
# Get auth attributes from the request scope
user_attributes = request.scope.get("user_attributes", {})
+ await log_request_pre_validation(request)
+
# Use context manager with both provider data and auth attributes
with request_provider_data_context(request.headers, user_attributes):
is_streaming = is_streaming_request(func.__name__, request, **kwargs)
diff --git a/llama_stack/providers/inline/agents/meta_reference/openai_responses.py b/llama_stack/providers/inline/agents/meta_reference/openai_responses.py
index 0690a15fe..8a9247a56 100644
--- a/llama_stack/providers/inline/agents/meta_reference/openai_responses.py
+++ b/llama_stack/providers/inline/agents/meta_reference/openai_responses.py
@@ -192,6 +192,7 @@ class OpenAIResponsesImpl:
status="completed",
output=output_messages,
)
+ logger.debug(f"OpenAI Responses response: {response}")
if store:
# Store in kvstore
@@ -218,7 +219,9 @@ class OpenAIResponsesImpl:
chat_tools: List[ChatCompletionToolParam] = []
for input_tool in tools:
# TODO: Handle other tool types
- if input_tool.type == "web_search":
+ if input_tool.type == "function":
+ chat_tools.append(ChatCompletionToolParam(type="function", function=input_tool.model_dump()))
+ elif input_tool.type == "web_search":
tool_name = "web_search"
tool = await self.tool_groups_api.get_tool(tool_name)
tool_def = ToolDefinition(