Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions src/xai_sdk/aio/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,28 @@ def _make_grpc_channel(
return channel

async def close(self) -> None:
"""Close method to properly clean up gRPC channels."""
"""Close method to properly clean up gRPC channels.

Ensures both channels are closed even if one fails, preventing resource leaks.
If both channels fail to close, the first exception is raised after attempting both.
"""
exceptions = []

if self._management_channel is not None:
await self._management_channel.close()
try:
await self._management_channel.close()
except Exception as e:
exceptions.append(e)

if self._api_channel is not None:
await self._api_channel.close()
try:
await self._api_channel.close()
except Exception as e:
exceptions.append(e)

if exceptions:
# Raise the first exception after attempting to close both channels
raise exceptions[0]

async def __aenter__(self):
"""Async context manager entry."""
Expand Down
9 changes: 9 additions & 0 deletions src/xai_sdk/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ def web_source(
Returns:
A `chat_pb2.Source` object configured for web search.

Raises:
ValueError: If both excluded_websites and allowed_websites are specified.

Example:
```
from xai_sdk.chat import SearchParameters, web_source
Expand All @@ -141,6 +144,12 @@ def web_source(
)
```
"""
if excluded_websites is not None and allowed_websites is not None:
raise ValueError(
"Cannot specify both 'excluded_websites' and 'allowed_websites'. "
"Please use only one parameter to either exclude or allow specific websites."
)

return chat_pb2.Source(
web=chat_pb2.WebSource(
country=country,
Expand Down
22 changes: 19 additions & 3 deletions src/xai_sdk/sync/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,28 @@ def _make_grpc_channel(
return channel

def close(self) -> None:
"""Close method to properly clean up gRPC channels."""
"""Close method to properly clean up gRPC channels.

Ensures both channels are closed even if one fails, preventing resource leaks.
If both channels fail to close, the first exception is raised after attempting both.
"""
exceptions = []

if self._management_channel is not None:
self._management_channel.close()
try:
self._management_channel.close()
except Exception as e:
exceptions.append(e)

if self._api_channel is not None:
self._api_channel.close()
try:
self._api_channel.close()
except Exception as e:
exceptions.append(e)

if exceptions:
# Raise the first exception after attempting to close both channels
raise exceptions[0]

def __enter__(self):
"""Context manager entry."""
Expand Down
18 changes: 18 additions & 0 deletions src/xai_sdk/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ def web_search(
Returns:
A `chat_pb2.Tool` object configured for web search.

Raises:
ValueError: If both excluded_domains and allowed_domains are specified.

Example:
```
from xai_sdk.tools import web_search
Expand All @@ -55,6 +58,12 @@ def web_search(
)
```
"""
if excluded_domains is not None and allowed_domains is not None:
raise ValueError(
"Cannot specify both 'excluded_domains' and 'allowed_domains'. "
"Please use only one parameter to either exclude or allow specific domains."
)

user_location = None
if (
user_location_country is not None
Expand Down Expand Up @@ -112,6 +121,9 @@ def x_search(
Returns:
A `chat_pb2.Tool` object configured for X search.

Raises:
ValueError: If both allowed_x_handles and excluded_x_handles are specified.

Example:
```
import datetime
Expand All @@ -126,6 +138,12 @@ def x_search(
)
```
"""
if allowed_x_handles is not None and excluded_x_handles is not None:
raise ValueError(
"Cannot specify both 'allowed_x_handles' and 'excluded_x_handles'. "
"Please use only one parameter to either allow or exclude specific X handles."
)

from_date_pb = Timestamp()
to_date_pb = Timestamp()

Expand Down
44 changes: 44 additions & 0 deletions src/xai_sdk/types/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,46 @@
]

ReasoningEffort: TypeAlias = Literal["low", "high"]
"""Reasoning effort level for models that support reasoning.

- "low": Uses fewer reasoning tokens, resulting in faster responses with less thorough analysis
- "high": Uses more reasoning tokens, resulting in slower responses with more thorough analysis
"""

ImageDetail: TypeAlias = Literal["auto", "low", "high"]
"""Image detail level for vision models.

- "auto": The system automatically selects an appropriate resolution
- "low": Uses low-resolution image processing, reducing token usage and increasing speed
- "high": Uses high-resolution image processing, increasing token usage but capturing more detail
"""

Content: TypeAlias = Union[str, chat_pb2.Content]
"""Content type for chat messages.

Can be either a plain string (automatically converted to text content) or a chat_pb2.Content
object (for images, files, or other structured content).
"""

ToolMode: TypeAlias = Literal["auto", "none", "required"]
"""Tool calling mode for chat requests.

- "auto": The model decides whether to call tools based on the conversation context (default)
- "none": The model will not call any tools and will only generate text responses
- "required": The model must call one or more tools before responding
"""


# json_schema purposefully omitted, since the `parse` method should be used when needing json_schema responses.
ResponseFormat: TypeAlias = Literal["text", "json_object"]
"""Response format type for chat completions.

- "text": The model returns plain text responses (default)
- "json_object": The model returns responses formatted as JSON objects

Note: For structured outputs with a specific JSON schema, use the `parse` method or pass a
Pydantic model to the `response_format` parameter instead.
"""

IncludeOption: TypeAlias = Literal[
"web_search_call_output",
Expand All @@ -31,6 +64,17 @@
"inline_citations",
"verbose_streaming",
]
"""Options for including additional information in chat responses.

- "web_search_call_output": Include detailed output from web search tool calls
- "x_search_call_output": Include detailed output from X (Twitter) search tool calls
- "code_execution_call_output": Include detailed output from code execution tool calls
- "collections_search_call_output": Include detailed output from collections search tool calls
- "attachment_search_call_output": Include detailed output from attachment search tool calls
- "mcp_call_output": Include detailed output from MCP (Model Context Protocol) tool calls
- "inline_citations": Include structured citation metadata with position information
- "verbose_streaming": Include additional streaming metadata for debugging
"""

IncludeOptionMap: dict[IncludeOption, "chat_pb2.IncludeOption"] = {
"web_search_call_output": chat_pb2.IncludeOption.INCLUDE_OPTION_WEB_SEARCH_CALL_OUTPUT,
Expand Down
Loading