245 lines
7.4 KiB
Python
245 lines
7.4 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
from functools import cached_property
|
|
from typing import TYPE_CHECKING, Mapping, Union
|
|
|
|
import httpx
|
|
from httpx import Timeout
|
|
from typing_extensions import override
|
|
|
|
if TYPE_CHECKING:
|
|
from zai.api_resource.agents import Agents
|
|
from zai.api_resource.assistant import Assistant
|
|
from zai.api_resource.audio import Audio
|
|
from zai.api_resource.batch import Batches
|
|
from zai.api_resource.chat import Chat
|
|
from zai.api_resource.embeddings import Embeddings
|
|
from zai.api_resource.files import Files
|
|
from zai.api_resource.images import Images
|
|
from zai.api_resource.moderations import Moderations
|
|
from zai.api_resource.tools import Tools
|
|
from zai.api_resource.videos import Videos
|
|
from zai.api_resource.voice import Voice
|
|
from zai.api_resource.web_search import WebSearchApi
|
|
from zai.api_resource.file_parser import FileParser
|
|
|
|
from .core import (
|
|
NOT_GIVEN,
|
|
ZAI_DEFAULT_MAX_RETRIES,
|
|
HttpClient,
|
|
NotGiven,
|
|
ZaiError,
|
|
_jwt_token,
|
|
)
|
|
|
|
|
|
class BaseClient(HttpClient):
|
|
"""
|
|
Main client for interacting with the ZAI API
|
|
|
|
Attributes:
|
|
chat (Chat): Chat completions API resource
|
|
api_key (str): API key for authentication
|
|
_disable_token_cache (bool): Whether to disable token caching
|
|
source_channel (str): Source channel identifier
|
|
"""
|
|
|
|
chat: Chat
|
|
api_key: str
|
|
base_url: str
|
|
disable_token_cache: bool = True
|
|
source_channel: str
|
|
|
|
def __init__(
|
|
self,
|
|
*,
|
|
api_key: str | None = None,
|
|
base_url: str | httpx.URL | None = None,
|
|
timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
|
|
max_retries: int = ZAI_DEFAULT_MAX_RETRIES,
|
|
http_client: httpx.Client | None = None,
|
|
custom_headers: Mapping[str, str] | None = None,
|
|
disable_token_cache: bool = True,
|
|
_strict_response_validation: bool = False,
|
|
source_channel: str | None = None,
|
|
) -> None:
|
|
"""
|
|
Initialize the ZAI client
|
|
|
|
Arguments:
|
|
api_key (str | None): API key for authentication.
|
|
If None, will try to get from ZAI_API_KEY environment variable.
|
|
base_url (str | httpx.URL | None): Base URL for the API.
|
|
If None, will try to get from ZAI_BASE_URL environment variable
|
|
timeout (Union[float, Timeout, None, NotGiven]): Request timeout configuration
|
|
max_retries (int): Maximum number of retries for failed requests
|
|
http_client (httpx.Client | None): Custom HTTP client to use
|
|
custom_headers (Mapping[str, str] | None): Additional headers to include in requests
|
|
disable_token_cache (bool): Whether to disable JWT token caching
|
|
_strict_response_validation (bool): Whether to enable strict response validation
|
|
source_channel (str | None): Source channel identifier
|
|
"""
|
|
if api_key is None:
|
|
api_key = os.environ.get('ZAI_API_KEY')
|
|
if api_key is None:
|
|
raise ZaiError('api_key not provided, please provide it through parameters or environment variables')
|
|
self.api_key = api_key
|
|
self.source_channel = source_channel
|
|
self.disable_token_cache = disable_token_cache
|
|
|
|
if base_url is None:
|
|
base_url = os.environ.get('ZAI_BASE_URL')
|
|
if base_url is None:
|
|
base_url = self.default_base_url
|
|
self.base_url = base_url
|
|
|
|
from ._version import __version__
|
|
|
|
super().__init__(
|
|
version=__version__,
|
|
base_url=base_url,
|
|
max_retries=max_retries,
|
|
timeout=timeout,
|
|
custom_httpx_client=http_client,
|
|
custom_headers=custom_headers,
|
|
_strict_response_validation=_strict_response_validation,
|
|
)
|
|
|
|
@property
|
|
def default_base_url(self):
|
|
raise NotImplementedError('Subclasses must define default_base_url')
|
|
|
|
@cached_property
|
|
def chat(self) -> Chat:
|
|
from zai.api_resource.chat import Chat
|
|
|
|
return Chat(self)
|
|
|
|
@cached_property
|
|
def assistant(self) -> Assistant:
|
|
from zai.api_resource.assistant import Assistant
|
|
|
|
return Assistant(self)
|
|
|
|
@cached_property
|
|
def agents(self) -> Agents:
|
|
from zai.api_resource.agents import Agents
|
|
|
|
return Agents(self)
|
|
|
|
@cached_property
|
|
def embeddings(self) -> Embeddings:
|
|
from zai.api_resource.embeddings import Embeddings
|
|
|
|
return Embeddings(self)
|
|
|
|
@cached_property
|
|
def batches(self) -> Batches:
|
|
from zai.api_resource.batch import Batches
|
|
|
|
return Batches(self)
|
|
|
|
@cached_property
|
|
def tools(self) -> Tools:
|
|
from zai.api_resource.tools import Tools
|
|
|
|
return Tools(self)
|
|
|
|
@cached_property
|
|
def web_search(self) -> WebSearchApi:
|
|
from zai.api_resource.web_search import WebSearchApi
|
|
|
|
return WebSearchApi(self)
|
|
|
|
@cached_property
|
|
def files(self) -> Files:
|
|
from zai.api_resource.files import Files
|
|
|
|
return Files(self)
|
|
|
|
@cached_property
|
|
def images(self) -> Images:
|
|
from zai.api_resource.images import Images
|
|
|
|
return Images(self)
|
|
|
|
@cached_property
|
|
def audio(self) -> Audio:
|
|
from zai.api_resource.audio import Audio
|
|
|
|
return Audio(self)
|
|
|
|
@cached_property
|
|
def videos(self) -> Videos:
|
|
from zai.api_resource.videos import Videos
|
|
|
|
return Videos(self)
|
|
|
|
@cached_property
|
|
def moderations(self) -> Moderations:
|
|
from zai.api_resource.moderations import Moderations
|
|
|
|
return Moderations(self)
|
|
|
|
@cached_property
|
|
def voice(self) -> Voice:
|
|
from zai.api_resource.voice import Voice
|
|
|
|
return Voice(self)
|
|
|
|
@cached_property
|
|
def file_parser(self) -> FileParser:
|
|
from zai.api_resource.file_parser import FileParser
|
|
return FileParser(self)
|
|
|
|
@property
|
|
@override
|
|
def auth_headers(self) -> dict[str, str]:
|
|
api_key = self.api_key
|
|
source_channel = self.source_channel or 'python-sdk'
|
|
if self.disable_token_cache:
|
|
return {
|
|
'Authorization': f'Bearer {api_key}',
|
|
'x-source-channel': source_channel,
|
|
}
|
|
else:
|
|
return {
|
|
'Authorization': f'Bearer {_jwt_token.generate_token(api_key)}',
|
|
'x-source-channel': source_channel,
|
|
}
|
|
|
|
def __del__(self) -> None:
|
|
if not hasattr(self, '_has_custom_http_client') or not hasattr(self, 'close') or not hasattr(self, '_client'):
|
|
# if the '__init__' method raised an error, self would not have client attr
|
|
return
|
|
|
|
if self._has_custom_http_client:
|
|
return
|
|
|
|
try:
|
|
# Check if client is still valid before closing
|
|
if hasattr(self, '_client') and self._client is not None:
|
|
self.close()
|
|
except Exception:
|
|
# Ignore any exceptions during cleanup to avoid masking the original error
|
|
pass
|
|
|
|
|
|
class ZaiClient(BaseClient):
|
|
@property
|
|
def default_base_url(self):
|
|
return 'https://api.z.ai/api/paas/v4'
|
|
|
|
@property
|
|
@override
|
|
def auth_headers(self) -> dict[str, str]:
|
|
headers = super().auth_headers
|
|
headers['Accept-Language'] = 'en-US,en'
|
|
return headers
|
|
|
|
|
|
class ZhipuAiClient(BaseClient):
|
|
@property
|
|
def default_base_url(self):
|
|
return 'https://open.bigmodel.cn/api/paas/v4'
|