Python: added HostedFileContent and HostedVectorStoreContent (#379)

* added HostedFileContent and HostedVectorStoreContent

* added convenience functions for hostedcodeinterpretertools

* fix docstrings

* udpated docstring

* clarified parsing logic

* updated type names

* updated test

* and vector stores
This commit is contained in:
Eduard van Valkenburg
2025-08-11 16:23:32 +02:00
committed by GitHub
Unverified
parent 82d39bc1f7
commit bbc07931c1
4 changed files with 416 additions and 3 deletions
+53 -1
View File
@@ -4,7 +4,7 @@ import inspect
from collections.abc import Awaitable, Callable
from functools import wraps
from time import perf_counter
from typing import Annotated, Any, Generic, Protocol, TypeVar, get_args, get_origin, runtime_checkable
from typing import TYPE_CHECKING, Annotated, Any, Generic, Protocol, TypeVar, get_args, get_origin, runtime_checkable
from opentelemetry import metrics, trace
from pydantic import BaseModel, Field, create_model
@@ -12,6 +12,9 @@ from pydantic import BaseModel, Field, create_model
from ._logging import get_logger
from .telemetry import GenAIAttributes, start_as_current_span
if TYPE_CHECKING:
from ._types import AIContents
tracer: trace.Tracer = trace.get_tracer("agent_framework")
meter: metrics.Meter = metrics.get_meter_provider().get_meter("agent_framework")
logger = get_logger()
@@ -216,6 +219,45 @@ def ai_function(
return decorator(func) if func else decorator # type: ignore[reportReturnType, return-value]
def _parse_inputs(
inputs: "AIContents | dict[str, Any] | str | list[AIContents | dict[str, Any] | str] | None",
) -> list["AIContents"]:
"""Parse the inputs for a tool, ensuring they are of type AIContents."""
if inputs is None:
return []
from ._types import AIContent, DataContent, HostedFileContent, HostedVectorStoreContent, UriContent
parsed_inputs: list["AIContents"] = []
if not isinstance(inputs, list):
inputs = [inputs]
for input_item in inputs:
if isinstance(input_item, str):
# If it's a string, we assume it's a URI or similar identifier.
# Convert it to a UriContent or similar type as needed.
parsed_inputs.append(UriContent(uri=input_item, media_type="text/plain"))
elif isinstance(input_item, dict):
# If it's a dict, we assume it contains properties for a specific content type.
# we check if the required keys are present to determine the type.
if "uri" in input_item:
parsed_inputs.append(
UriContent(**input_item) if "media_type" in input_item else DataContent(**input_item)
)
elif "file_id" in input_item:
parsed_inputs.append(HostedFileContent(**input_item))
elif "vector_store_id" in input_item:
parsed_inputs.append(HostedVectorStoreContent(**input_item))
elif "data" in input_item:
parsed_inputs.append(DataContent(**input_item))
else:
raise ValueError(f"Unsupported input type: {input_item}")
elif isinstance(input_item, AIContent):
parsed_inputs.append(input_item)
else:
raise TypeError(f"Unsupported input type: {type(input_item).__name__}. Expected AIContents or dict.")
return parsed_inputs
class HostedCodeInterpreterTool(AITool):
"""Represents a hosted tool that can be specified to an AI service to enable it to execute generated code.
@@ -226,6 +268,7 @@ class HostedCodeInterpreterTool(AITool):
def __init__(
self,
name: str = "code_interpreter",
inputs: "AIContents | dict[str, Any] | str | list[AIContents | dict[str, Any] | str] | None" = None,
description: str | None = None,
additional_properties: dict[str, Any] | None = None,
):
@@ -233,10 +276,19 @@ class HostedCodeInterpreterTool(AITool):
Args:
name: The name of the tool. Defaults to "code_interpreter".
inputs: A list of contents that the tool can accept as input. Defaults to None.
This should mostly be HostedFileContent or HostedVectorStoreContent.
Can also be DataContent, depending on the service used.
When supplying a list, it can contain:
- AIContents instances
- dicts with properties for AIContents (e.g., {"uri": "http://example.com", "media_type": "text/html"})
- strings (which will be converted to UriContent with media_type "text/plain").
If None, defaults to an empty list.
description: A description of the tool.
additional_properties: Additional properties associated with the tool, specific to the service used.
"""
self.name = name
self.inputs = _parse_inputs(inputs)
self.description = description
self.additional_properties = additional_properties
+67 -1
View File
@@ -99,6 +99,8 @@ __all__ = [
"FunctionCallContent",
"FunctionResultContent",
"GeneratedEmbeddings",
"HostedFileContent",
"HostedVectorStoreContent",
"SpeechToTextOptions",
"StructuredResponse",
"TextContent",
@@ -1036,6 +1038,68 @@ class UsageContent(AIContent):
)
class HostedFileContent(AIContent):
"""Represents a hosted file content.
Attributes:
file_id: The identifier of the hosted file.
type: The type of content, which is always "hosted_file" for this class.
additional_properties: Optional additional properties associated with the content.
raw_representation: Optional raw representation of the content.
"""
type: Literal["hosted_file"] = "hosted_file" # type: ignore[assignment]
file_id: str
def __init__(
self,
file_id: str,
*,
additional_properties: dict[str, Any] | None = None,
raw_representation: Any | None = None,
**kwargs: Any,
) -> None:
"""Initializes a HostedFileContent instance."""
super().__init__(
file_id=file_id, # type: ignore[reportCallIssue]
additional_properties=additional_properties,
raw_representation=raw_representation,
**kwargs,
)
class HostedVectorStoreContent(AIContent):
"""Represents a hosted vector store content.
Attributes:
vector_store_id: The identifier of the hosted vector store.
type: The type of content, which is always "hosted_vector_store" for this class.
additional_properties: Optional additional properties associated with the content.
raw_representation: Optional raw representation of the content.
"""
type: Literal["hosted_vector_store"] = "hosted_vector_store" # type: ignore[assignment]
vector_store_id: str
def __init__(
self,
vector_store_id: str,
*,
additional_properties: dict[str, Any] | None = None,
raw_representation: Any | None = None,
**kwargs: Any,
) -> None:
"""Initializes a HostedVectorStoreContent instance."""
super().__init__(
vector_store_id=vector_store_id, # type: ignore[reportCallIssue]
additional_properties=additional_properties,
raw_representation=raw_representation,
**kwargs,
)
AIContents = Annotated[
TextContent
| DataContent
@@ -1044,7 +1108,9 @@ AIContents = Annotated[
| FunctionCallContent
| FunctionResultContent
| ErrorContent
| UsageContent,
| UsageContent
| HostedFileContent
| HostedVectorStoreContent,
Field(discriminator="type"),
]