diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 0193efb..0000000 --- a/.flake8 +++ /dev/null @@ -1,16 +0,0 @@ -[flake8] -# E501 string literal is too long -# W503 line break before binary operator -# E203 whitespace before ':' (triggered on list slices like xs[i : i + 5]) -# E704 multiple statements on one line (def) -ignore = E501, W503, E203, E704 -max-line-length = 80 -exclude = - .git, - .tmp, - .venv, - .conda, - .nox, - .pytest_cache - __pycache__, - __init__.py diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 9ea646d..86f34a7 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,8 +1,9 @@ { "recommendations": [ "ms-python.python", - "ms-python.black-formatter", - "ms-python.isort", - "DavidAnson.vscode-markdownlint" + "charliermarsh.ruff", + "tamasfe.even-better-toml", + "davidanson.vscode-markdownlint", + "streetsidesoftware.code-spell-checker" ] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 5562909..672ecfd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,16 +1,26 @@ { - "files.trimTrailingWhitespace": true, + "[toml]": { + "editor.formatOnSave": true, + "editor.tabSize": 4, + }, + "[json]": { + "editor.formatOnSave": true, + }, "[python]": { - "editor.defaultFormatter": "ms-python.black-formatter", + "editor.defaultFormatter": "charliermarsh.ruff", "editor.formatOnSave": true, + "editor.tabSize": 4, "editor.codeActionsOnSave": { - "source.organizeImports": "explicit" - }, - "editor.tabSize": 4 + "source.organizeImports.ruff": "explicit", + "source.fixAll.ruff": "explicit" + } }, - "python.testing.pytestArgs": ["."], + "python.testing.pytestArgs": [ + "tests" + ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, - "python.analysis.typeCheckingMode": "basic" + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true, + "files.trimTrailingWhitespace": true, } - diff --git a/Makefile b/Makefile index f128188..922e8b1 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VENV_DIR ?= .venv POETRY ?= poetry POETRY_PYTHON ?= python -.PHONY: all init_env install clean lint format test spell_check +.PHONY: all init_env install clean lint format test -include .env export @@ -51,4 +51,3 @@ help: @echo '-- LINTING --' @echo 'format - run code formatters' @echo 'lint - run linters' - @echo 'spell_check - run spell check' diff --git a/aidial_client/_auth.py b/aidial_client/_auth.py index 5350fcf..d31cfb7 100644 --- a/aidial_client/_auth.py +++ b/aidial_client/_auth.py @@ -1,12 +1,13 @@ +from collections.abc import Awaitable, Callable from inspect import isawaitable -from typing import Awaitable, Callable, Dict, Optional, TypeVar, Union +from typing import TypeVar -SyncAuthValue = Union[str, Callable[[], str]] -AsyncAuthValue = Union[SyncAuthValue, Callable[[], Awaitable[str]]] +SyncAuthValue = str | Callable[[], str] +AsyncAuthValue = SyncAuthValue | Callable[[], Awaitable[str]] AuthValueT = TypeVar( "AuthValueT", - bound=Union[SyncAuthValue, AsyncAuthValue], + bound=SyncAuthValue | AsyncAuthValue, ) @@ -20,7 +21,8 @@ def get_auth_value(auth_value: SyncAuthValue) -> str: if TYPE_CHECKING: assert_never(auth_value) raise TypeError( - f"auth_value must be a string or a callable returning a string, got {type(auth_value).__name__}" + f"auth_value must be a string or a callable returning a string, " + f"got {type(auth_value).__name__}" ) @@ -35,16 +37,17 @@ async def aget_auth_value(auth_value: AsyncAuthValue) -> str: if TYPE_CHECKING: assert_never(auth_value) raise TypeError( - f"auth_value must be a string or a callable, got {type(auth_value).__name__}" + "auth_value must be a string or a callable, " + f"got {type(auth_value).__name__}" ) def get_combined_auth_headers( *, - api_key: Optional[SyncAuthValue] = None, - bearer_token: Optional[SyncAuthValue] = None, -) -> Dict[str, str]: - headers: Dict[str, str] = {} + api_key: SyncAuthValue | None = None, + bearer_token: SyncAuthValue | None = None, +) -> dict[str, str]: + headers: dict[str, str] = {} if api_key is not None: headers["api-key"] = get_auth_value(api_key) @@ -58,11 +61,14 @@ def get_combined_auth_headers( async def aget_combined_auth_headers( *, - api_key: Optional[AsyncAuthValue] = None, - bearer_token: Optional[AsyncAuthValue] = None, -) -> Dict[str, str]: - """Get combined authentication headers from both api_key and bearer_token (async).""" - headers: Dict[str, str] = {} + api_key: AsyncAuthValue | None = None, + bearer_token: AsyncAuthValue | None = None, +) -> dict[str, str]: + """ + Get combined authentication headers from both api_key and + bearer_token (async). + """ + headers: dict[str, str] = {} if api_key is not None: processed_api_key = await aget_auth_value(api_key) @@ -77,8 +83,8 @@ async def aget_combined_auth_headers( def validate_auth( *, - api_key: Optional[AsyncAuthValue] = None, - bearer_token: Optional[AsyncAuthValue] = None, + api_key: AsyncAuthValue | None = None, + bearer_token: AsyncAuthValue | None = None, ) -> None: """Validate that at least one authentication method is provided.""" if not api_key and not bearer_token: diff --git a/aidial_client/_client.py b/aidial_client/_client.py index 5c4907d..4f8cd70 100644 --- a/aidial_client/_client.py +++ b/aidial_client/_client.py @@ -1,6 +1,6 @@ from abc import ABC, abstractmethod from pathlib import PurePosixPath -from typing import Dict, Generic, Optional, TypeVar, Union +from typing import Generic, TypeVar from urllib.parse import urljoin import openai @@ -24,30 +24,28 @@ from aidial_client.helpers._url import enforce_trailing_slash from aidial_client.types.bucket import AppData -_HttpClientT = TypeVar( - "_HttpClientT", bound=Union[AsyncHTTPClient, SyncHTTPClient] -) +_HttpClientT = TypeVar("_HttpClientT", bound=AsyncHTTPClient | SyncHTTPClient) class BaseDialClient(Generic[_HttpClientT, AuthValueT], ABC): - _api_key: Optional[AuthValueT] - _bearer_token: Optional[AuthValueT] + _api_key: AuthValueT | None + _bearer_token: AuthValueT | None _base_url: str _http_client: _HttpClientT - _auth_headers: Dict[str, str] - _my_bucket: Optional[str] - _my_appdata: Union[AppData, None, NotGiven] + _auth_headers: dict[str, str] + _my_bucket: str | None + _my_appdata: AppData | None | NotGiven def __init__( self, *, base_url: str, - api_key: Optional[AuthValueT] = None, - bearer_token: Optional[AuthValueT] = None, + api_key: AuthValueT | None = None, + bearer_token: AuthValueT | None = None, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: Union[float, Timeout, None] = DEFAULT_TIMEOUT, - api_version: Optional[str] = None, - http_client: Optional[_HttpClientT] = None, + timeout: float | Timeout | None = DEFAULT_TIMEOUT, + api_version: str | None = None, + http_client: _HttpClientT | None = None, ): validate_auth(api_key=api_key, bearer_token=bearer_token) self._api_key = api_key @@ -79,12 +77,11 @@ def base_url(self) -> str: return self._base_url @property - def api_version(self) -> Optional[str]: + def api_version(self) -> str | None: return self._api_version class Dial(BaseDialClient[SyncHTTPClient, SyncAuthValue]): - def _init_resources(self) -> None: openai_client = openai.AzureOpenAI( api_key="-", @@ -150,26 +147,25 @@ def my_conversations_home(self) -> PurePosixPath: def my_prompts_home(self) -> PurePosixPath: return "prompts" / PurePosixPath(self.my_bucket()) - def _get_my_appdata(self) -> Optional[AppData]: + def _get_my_appdata(self) -> AppData | None: return self.bucket.get_appdata() - def my_appdata(self) -> Optional[AppData]: + def my_appdata(self) -> AppData | None: if isinstance(self._my_appdata, NotGiven): self._my_appdata = self._get_my_appdata() return self._my_appdata - def my_appdata_home(self) -> Optional[PurePosixPath]: + def my_appdata_home(self) -> PurePosixPath | None: appdata = self.my_appdata() if appdata: return PurePosixPath(appdata.raw) return None - def auth_headers(self) -> Dict[str, str]: + def auth_headers(self) -> dict[str, str]: return self._http_client.auth_headers() class AsyncDial(BaseDialClient[AsyncHTTPClient, AsyncAuthValue]): - def _init_resources(self) -> None: openai_client = openai.AsyncAzureOpenAI( api_key="-", @@ -241,19 +237,19 @@ async def my_conversations_home(self) -> PurePosixPath: async def my_prompts_home(self) -> PurePosixPath: return "prompts" / PurePosixPath(await self.my_bucket()) - async def _get_my_appdata(self) -> Optional[AppData]: + async def _get_my_appdata(self) -> AppData | None: return await self.bucket.get_appdata() - async def my_appdata(self) -> Optional[AppData]: + async def my_appdata(self) -> AppData | None: if isinstance(self._my_appdata, NotGiven): self._my_appdata = await self._get_my_appdata() return self._my_appdata - async def my_appdata_home(self) -> Optional[PurePosixPath]: + async def my_appdata_home(self) -> PurePosixPath | None: appdata = await self.my_appdata() if appdata: return PurePosixPath(appdata.raw) return None - async def auth_headers(self) -> Dict[str, str]: + async def auth_headers(self) -> dict[str, str]: return await self._http_client.auth_headers() diff --git a/aidial_client/_client_pool.py b/aidial_client/_client_pool.py index c2b9837..7b15b72 100644 --- a/aidial_client/_client_pool.py +++ b/aidial_client/_client_pool.py @@ -1,5 +1,3 @@ -from typing import Optional, Union - import httpx from aidial_client._auth import AsyncAuthValue, SyncAuthValue @@ -27,10 +25,10 @@ def create_client( self, *, base_url: str, - api_key: Optional[SyncAuthValue] = None, - bearer_token: Optional[SyncAuthValue] = None, + api_key: SyncAuthValue | None = None, + bearer_token: SyncAuthValue | None = None, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: Union[httpx.Timeout, float] = DEFAULT_TIMEOUT, + timeout: httpx.Timeout | float = DEFAULT_TIMEOUT, ) -> Dial: return Dial( base_url=base_url, @@ -62,10 +60,10 @@ def create_client( self, *, base_url: str, - api_key: Optional[AsyncAuthValue] = None, - bearer_token: Optional[AsyncAuthValue] = None, + api_key: AsyncAuthValue | None = None, + bearer_token: AsyncAuthValue | None = None, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: Union[httpx.Timeout, float] = DEFAULT_TIMEOUT, + timeout: httpx.Timeout | float = DEFAULT_TIMEOUT, ) -> AsyncDial: return AsyncDial( base_url=base_url, diff --git a/aidial_client/_exception.py b/aidial_client/_exception.py index aed54ff..98acf24 100644 --- a/aidial_client/_exception.py +++ b/aidial_client/_exception.py @@ -1,5 +1,5 @@ +from collections.abc import Mapping from http import HTTPStatus -from typing import Mapping, Optional class DialException(Exception): @@ -7,10 +7,10 @@ def __init__( self, message: str, status_code: int = 500, - type: Optional[str] = "runtime_error", - param: Optional[str] = None, - code: Optional[str] = None, - display_message: Optional[str] = None, + type: str | None = "runtime_error", + param: str | None = None, + code: str | None = None, + display_message: str | None = None, ) -> None: self.message = message self.status_code = status_code diff --git a/aidial_client/_http_client/_async.py b/aidial_client/_http_client/_async.py index 5dcf37c..d7d7765 100644 --- a/aidial_client/_http_client/_async.py +++ b/aidial_client/_http_client/_async.py @@ -1,16 +1,8 @@ import asyncio -from contextlib import asynccontextmanager +from collections.abc import AsyncIterator, Callable, Mapping +from contextlib import asynccontextmanager, suppress from http import HTTPStatus -from typing import ( - Any, - AsyncIterator, - Callable, - Dict, - Mapping, - Optional, - Type, - Union, -) +from typing import Any import httpx @@ -30,7 +22,7 @@ def _create_internal_client(self) -> httpx.AsyncClient: timeout=self._timeout, ) - async def auth_headers(self) -> Dict[str, str]: + async def auth_headers(self) -> dict[str, str]: return await aget_combined_auth_headers( api_key=self._api_key, bearer_token=self._bearer_token ) @@ -39,7 +31,7 @@ async def _retry_request( self, *, options: FinalRequestOptions, - cast_to: Type[ResponseT], + cast_to: type[ResponseT], remaining_retries: int, ) -> ResponseT: remaining = remaining_retries - 1 @@ -57,11 +49,10 @@ async def request( self, *, options: FinalRequestOptions, - cast_to: Type[ResponseT], - remaining_retries: Optional[int] = None, - on_http_error: Optional[ - Callable[[httpx.HTTPStatusError], Optional[DialException]] - ] = None, + cast_to: type[ResponseT], + remaining_retries: int | None = None, + on_http_error: Callable[[httpx.HTTPStatusError], DialException | None] + | None = None, ) -> ResponseT: retries = self._remaining_retries(remaining_retries, options) auth_headers = await self.auth_headers() @@ -127,8 +118,8 @@ async def stream_sse( method: str, url: str, json_data: Any, - headers: Optional[Mapping[str, str]] = None, - timeout: Union[float, httpx.Timeout, None, NotGiven] = NOT_GIVEN, + headers: Mapping[str, str] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncIterator[httpx.Response]: """Open an SSE streaming response. Yields the open httpx.Response. @@ -155,10 +146,8 @@ async def stream_sse( try: response.raise_for_status() except httpx.HTTPStatusError as err: - try: + with suppress(httpx.HTTPError): await response.aread() - except httpx.HTTPError: - pass raise self._make_dial_error_from_response( err.response ) from err diff --git a/aidial_client/_http_client/_base.py b/aidial_client/_http_client/_base.py index 19adf22..7723c53 100644 --- a/aidial_client/_http_client/_base.py +++ b/aidial_client/_http_client/_base.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from http import HTTPStatus from random import uniform -from typing import Dict, Generic, Optional, TypeVar, Union +from typing import Generic, TypeVar import httpx @@ -13,23 +13,23 @@ from aidial_client.helpers._url import enforce_trailing_slash _HttpInternalClientT = TypeVar( - "_HttpInternalClientT", bound=Union[httpx.Client, httpx.AsyncClient] + "_HttpInternalClientT", bound=httpx.Client | httpx.AsyncClient ) class BaseHTTPClient(ABC, Generic[_HttpInternalClientT, AuthValueT]): _internal_http_client: _HttpInternalClientT - _api_key: Optional[AuthValueT] - _bearer_token: Optional[AuthValueT] + _api_key: AuthValueT | None + _bearer_token: AuthValueT | None def __init__( self, base_url: str, - api_key: Optional[AuthValueT], - bearer_token: Optional[AuthValueT], + api_key: AuthValueT | None, + bearer_token: AuthValueT | None, max_retries: int, - timeout: Union[float, httpx.Timeout, None], - internal_http_client: Optional[_HttpInternalClientT] = None, + timeout: float | httpx.Timeout | None, + internal_http_client: _HttpInternalClientT | None = None, ): self.base_url = httpx.URL(enforce_trailing_slash(base_url)) self._api_key = api_key @@ -57,7 +57,7 @@ def _prepare_url(self, url: str) -> httpx.URL: def _build_request( self, options: FinalRequestOptions, - auth_headers: Dict[str, str], + auth_headers: dict[str, str], ) -> httpx.Request: custom_headers = options.headers or {} return self._internal_http_client.build_request( @@ -88,10 +88,7 @@ def _should_retry(self, response: httpx.Response) -> bool: if response.status_code == HTTPStatus.CONFLICT: return True - if response.status_code == HTTPStatus.TOO_MANY_REQUESTS: - return True - - return False + return response.status_code == HTTPStatus.TOO_MANY_REQUESTS def _calculate_retry_sleep_seconds( self, @@ -106,7 +103,7 @@ def _calculate_retry_sleep_seconds( sleep_seconds = min( INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY ) - timeout = sleep_seconds + uniform(-0.5, 0.5) + timeout = sleep_seconds + uniform(-0.5, 0.5) # noqa: S311 return max(0, timeout) def _make_dial_error_from_response( diff --git a/aidial_client/_http_client/_sse.py b/aidial_client/_http_client/_sse.py index fa776bb..7e03acd 100644 --- a/aidial_client/_http_client/_sse.py +++ b/aidial_client/_http_client/_sse.py @@ -1,4 +1,4 @@ -from typing import AsyncIterator, Iterator, List +from collections.abc import AsyncIterator, Iterator from aidial_client._log import logger @@ -9,20 +9,23 @@ def _strip_field(line: str, prefix: str) -> str: - """Strip a single leading U+0020 SPACE after the field colon, per the SSE spec.""" + """ + Strip a single leading U+0020 SPACE after the field colon, per the SSE spec. + """ value = line[len(prefix) :] - return value[1:] if value.startswith(" ") else value + return value.removeprefix(" ") def iter_data_events(lines: Iterator[str]) -> Iterator[str]: - """Yield the payload of each complete ``data:`` event from an SSE line stream. + """ + Yield the payload of each complete ``data:`` event from an SSE line stream. An event is complete when a blank line follows the ``data:`` line(s). Per the SSE dispatch rule, a buffer that has not been terminated by a blank line is discarded (we do NOT flush partial events at end of stream). Comment lines (``:``) and other field names are ignored. """ - buffer: List[str] = [] + buffer: list[str] = [] for line in lines: if line == "": if buffer: @@ -35,7 +38,7 @@ def iter_data_events(lines: Iterator[str]) -> Iterator[str]: async def aiter_data_events(lines: AsyncIterator[str]) -> AsyncIterator[str]: - buffer: List[str] = [] + buffer: list[str] = [] async for line in lines: if line == "": if buffer: diff --git a/aidial_client/_http_client/_sync.py b/aidial_client/_http_client/_sync.py index 583de97..40e8cff 100644 --- a/aidial_client/_http_client/_sync.py +++ b/aidial_client/_http_client/_sync.py @@ -1,7 +1,8 @@ import time -from contextlib import contextmanager +from collections.abc import Callable, Iterator, Mapping +from contextlib import contextmanager, suppress from http import HTTPStatus -from typing import Any, Callable, Dict, Iterator, Mapping, Optional, Type, Union +from typing import Any import httpx @@ -22,7 +23,7 @@ def _create_internal_client(self) -> httpx.Client: def _retry_request( self, options: FinalRequestOptions, - cast_to: Type[ResponseT], + cast_to: type[ResponseT], remaining_retries: int, ) -> ResponseT: remaining = remaining_retries - 1 @@ -38,7 +39,7 @@ def _retry_request( remaining_retries=remaining, ) - def auth_headers(self) -> Dict[str, str]: + def auth_headers(self) -> dict[str, str]: return get_combined_auth_headers( api_key=self._api_key, bearer_token=self._bearer_token ) @@ -46,12 +47,11 @@ def auth_headers(self) -> Dict[str, str]: def request( self, *, - cast_to: Type[ResponseT], + cast_to: type[ResponseT], options: FinalRequestOptions, - remaining_retries: Optional[int] = None, - on_http_error: Optional[ - Callable[[httpx.HTTPStatusError], Optional[DialException]] - ] = None, + remaining_retries: int | None = None, + on_http_error: Callable[[httpx.HTTPStatusError], DialException | None] + | None = None, ) -> ResponseT: retries = self._remaining_retries(remaining_retries, options) auth_headers = self.auth_headers() @@ -118,8 +118,8 @@ def stream_sse( method: str, url: str, json_data: Any, - headers: Optional[Mapping[str, str]] = None, - timeout: Union[float, httpx.Timeout, None, NotGiven] = NOT_GIVEN, + headers: Mapping[str, str] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Iterator[httpx.Response]: """Open an SSE streaming response. Yields the open httpx.Response. @@ -146,10 +146,8 @@ def stream_sse( try: response.raise_for_status() except httpx.HTTPStatusError as err: - try: + with suppress(httpx.HTTPError): response.read() - except httpx.HTTPError: - pass raise self._make_dial_error_from_response( err.response ) from err diff --git a/aidial_client/_internal_types/_generic.py b/aidial_client/_internal_types/_generic.py index 113e9db..6edc108 100644 --- a/aidial_client/_internal_types/_generic.py +++ b/aidial_client/_internal_types/_generic.py @@ -1,4 +1,4 @@ -from typing import TypeVar, Union +from typing import TypeVar import httpx @@ -10,15 +10,13 @@ ResponseT = TypeVar( "ResponseT", - bound=Union[ - ExtraAllowModel, - ExtraForbidModel, - bytes, - str, - dict, - httpx.Response, - FileDownloadResponse, - None, - ], + bound=ExtraAllowModel + | ExtraForbidModel + | bytes + | str + | dict + | httpx.Response + | FileDownloadResponse + | None, ) NoneType = type(None) diff --git a/aidial_client/_internal_types/_http_request.py b/aidial_client/_internal_types/_http_request.py index da93638..25a47f1 100644 --- a/aidial_client/_internal_types/_http_request.py +++ b/aidial_client/_internal_types/_http_request.py @@ -1,13 +1,9 @@ +from collections.abc import Mapping, Sequence from io import BufferedReader from typing import ( IO, Any, Literal, - Mapping, - Optional, - Sequence, - Tuple, - Union, final, ) @@ -16,29 +12,29 @@ from aidial_client._compatibility.pydantic_v1 import BaseModel from aidial_client._internal_types._defaults import NOT_GIVEN, NotGiven -FileContent = Union[ - IO[bytes], - bytes, - str, - # Somehow, pydantic doesn't recognize result of open('...', 'rb') as IO[bytes] - # even though BufferedReader is a subclass of IO[bytes] - BufferedReader, -] -FileTypes = Union[ +FileContent = ( + IO[bytes] + | bytes + | str + # Somehow, pydantic doesn't recognize result of open('...', 'rb') + # as IO[bytes] even though BufferedReader is a subclass of IO[bytes] + | BufferedReader +) +FileTypes = ( # file (or bytes) - FileContent, + FileContent # (filename, file (or bytes)) - Tuple[Optional[str], FileContent], + | tuple[str | None, FileContent] # (filename, file (or bytes), content_type) - Tuple[Optional[str], FileContent, Optional[str]], + | tuple[str | None, FileContent, str | None] # (filename, file (or bytes), content_type, headers) - Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], -] + | tuple[str | None, FileContent, str | None, Mapping[str, str]] +) Params = Mapping[str, Any] Headers = Mapping[str, Any] Data = Mapping[str, Any] -RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] +RequestFiles = Mapping[str, FileTypes] | Sequence[tuple[str, FileTypes]] @final @@ -48,12 +44,12 @@ class Config: method: Literal["GET", "PUT", "POST", "DELETE"] url: str - params: Optional[Params] = None - headers: Optional[Headers] = None - max_retries: Union[int, NotGiven] = NOT_GIVEN - timeout: Union[float, Timeout, NotGiven, None] = NOT_GIVEN - files: Optional[RequestFiles] = None - json_data: Optional[Data] = None + params: Params | None = None + headers: Headers | None = None + max_retries: int | NotGiven = NOT_GIVEN + timeout: float | Timeout | NotGiven | None = NOT_GIVEN + files: RequestFiles | None = None + json_data: Data | None = None def get_max_retries(self, max_retries: int) -> int: if isinstance(self.max_retries, NotGiven): @@ -61,8 +57,8 @@ def get_max_retries(self, max_retries: int) -> int: return self.max_retries def get_timeout( - self, timeout: Union[float, Timeout, None] - ) -> Union[float, Timeout, None]: + self, timeout: float | Timeout | None + ) -> float | Timeout | None: if isinstance(self.timeout, NotGiven): return timeout return self.timeout diff --git a/aidial_client/_internal_types/_json_rpc.py b/aidial_client/_internal_types/_json_rpc.py index 90b9ffa..2aaf447 100644 --- a/aidial_client/_internal_types/_json_rpc.py +++ b/aidial_client/_internal_types/_json_rpc.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Literal, Optional, Union +from typing import Any, Literal from aidial_client._compatibility.pydantic_v1 import ( BaseModel, @@ -11,7 +11,7 @@ class JsonRpcError(BaseModel): code: int message: str - data: Optional[Any] = None + data: Any | None = None class Config: extra = Extra.allow @@ -20,8 +20,8 @@ class Config: class JsonRpcRequest(BaseModel): jsonrpc: Literal["2.0"] = "2.0" method: str - params: Optional[Union[List[Any], Dict[str, Any]]] = None - id: Optional[Union[int, str]] = None + params: list[Any] | dict[str, Any] | None = None + id: int | str | None = None class Config: smart_union = True @@ -29,9 +29,9 @@ class Config: class JsonRpcResponse(BaseModel): jsonrpc: Literal["2.0"] - result: Optional[Any] = None - error: Optional[JsonRpcError] = None - id: Optional[Union[int, str]] = Field(...) + result: Any | None = None + error: JsonRpcError | None = None + id: int | str | None = Field(...) class Config: smart_union = True @@ -39,9 +39,11 @@ class Config: @root_validator(pre=True) def _validate_result_xor_error(cls, values): - """Per JSON-RPC 2.0 (https://www.jsonrpc.org/specification#response_object), - either ``result`` or ``error`` MUST be included (presence-wise — ``null`` - is a valid result value), and both MUST NOT be included. + """ + As per JSON-RPC 2.0 (https://www.jsonrpc.org/specification#response_object), + either ``result`` or ``error`` MUST be included + (presence-wise — ``null`` is a valid result value), + and both MUST NOT be included. """ if not isinstance(values, dict): return values @@ -63,13 +65,13 @@ class JsonRpcResponses(BaseModel): a batch array, normalizing both to a list via the ``responses`` property. """ - __root__: Union[JsonRpcResponse, List[JsonRpcResponse]] + __root__: JsonRpcResponse | list[JsonRpcResponse] class Config: smart_union = True @property - def responses(self) -> List[JsonRpcResponse]: + def responses(self) -> list[JsonRpcResponse]: if isinstance(self.__root__, list): return self.__root__ return [self.__root__] diff --git a/aidial_client/_utils/_dict.py b/aidial_client/_utils/_dict.py index e7c6b5f..019c51b 100644 --- a/aidial_client/_utils/_dict.py +++ b/aidial_client/_utils/_dict.py @@ -1,5 +1,5 @@ -from typing import Any, Dict, Union +from typing import Any -def remove_none(input: Dict[str, Union[Any, None]]) -> Dict[str, Any]: +def remove_none(input: dict[str, Any | None]) -> dict[str, Any]: return {key: value for key, value in input.items() if value is not None} diff --git a/aidial_client/_utils/_openai.py b/aidial_client/_utils/_openai.py index 545a474..43a665d 100644 --- a/aidial_client/_utils/_openai.py +++ b/aidial_client/_utils/_openai.py @@ -1,4 +1,4 @@ -from typing import AsyncIterator, Iterator +from collections.abc import AsyncIterator, Iterator import openai from openai.types.chat import ChatCompletion as OpenAIChatCompletion diff --git a/aidial_client/_utils/_response_processing.py b/aidial_client/_utils/_response_processing.py index 5135397..446c4af 100644 --- a/aidial_client/_utils/_response_processing.py +++ b/aidial_client/_utils/_response_processing.py @@ -1,4 +1,4 @@ -from typing import Type, cast +from typing import cast import httpx @@ -11,24 +11,24 @@ def process_block_response( - cast_to: Type[ResponseT], response: httpx.Response + cast_to: type[ResponseT], response: httpx.Response ) -> ResponseT: if cast_to == httpx.Response: return cast(ResponseT, response) - elif cast_to == bytes: + elif cast_to is bytes: return cast(ResponseT, response.content) - elif cast_to == str: + elif cast_to is str: return cast(ResponseT, response.text) elif cast_to == NoneType: return cast(ResponseT, None) - elif cast_to == dict: + elif cast_to is dict: try: return cast(ResponseT, response.json()) except Exception as e: raise ParsingDataError( message=f"Error during parsing of response data: {str(e)}" ) - elif issubclass(cast_to, (ExtraForbidModel, ExtraAllowModel)): + elif issubclass(cast_to, ExtraForbidModel | ExtraAllowModel): try: data = response.json() return cast_to(**data) diff --git a/aidial_client/_utils/_type_guard.py b/aidial_client/_utils/_type_guard.py index b7e6b19..5a448b5 100644 --- a/aidial_client/_utils/_type_guard.py +++ b/aidial_client/_utils/_type_guard.py @@ -1,6 +1,5 @@ -from typing import Mapping - -from typing_extensions import TypeGuard +from collections.abc import Mapping +from typing import TypeGuard def is_mapping(obj) -> TypeGuard[Mapping]: diff --git a/aidial_client/helpers/storage_resource.py b/aidial_client/helpers/storage_resource.py index 190e4b0..3bf00e5 100644 --- a/aidial_client/helpers/storage_resource.py +++ b/aidial_client/helpers/storage_resource.py @@ -1,5 +1,5 @@ from pathlib import PurePosixPath -from typing import Literal, Optional, Union, cast, get_args +from typing import Literal, cast, get_args from urllib.parse import urljoin, urlparse from aidial_client._compatibility.pydantic_v1 import BaseModel @@ -36,15 +36,15 @@ class DialStorageResource(BaseModel): Filename, like 'my-file.txt' None for a directory """ - filename: Optional[str] = None + filename: str | None = None def safe_parse_storage_resource( *, url: str, dial_api_url: str, - expected_resource_type: Optional[StorageResourceType] = None, -) -> Union[DialStorageResource, NotDialURLError, InvalidDialURLError]: + expected_resource_type: StorageResourceType | None = None, +) -> DialStorageResource | NotDialURLError | InvalidDialURLError: """ Parse the storage resource from the URL, that could be 1. Absolute: "https://dial.core/v1/files/my-bucket/my-file.txt" @@ -108,14 +108,14 @@ def parse_storage_resource( *, url: str, dial_api_url: str, - expected_resource_type: Optional[StorageResourceType] = None, + expected_resource_type: StorageResourceType | None = None, ) -> DialStorageResource: result = safe_parse_storage_resource( url=url, dial_api_url=dial_api_url, expected_resource_type=expected_resource_type, ) - if isinstance(result, (NotDialURLError, InvalidDialURLError)): + if isinstance(result, NotDialURLError | InvalidDialURLError): raise result return result diff --git a/aidial_client/resources/application.py b/aidial_client/resources/application.py index 9146934..fd460a3 100644 --- a/aidial_client/resources/application.py +++ b/aidial_client/resources/application.py @@ -1,4 +1,3 @@ -from typing import List from urllib.parse import urljoin from aidial_client._constants import APPLICATION_PREFIX @@ -23,7 +22,7 @@ def _list_raw(self) -> ApplicationsResponse: options=FinalRequestOptions(method="GET", url=APPLICATION_PREFIX), ) - def list(self) -> List[ApplicationType]: + def list(self) -> list[ApplicationType]: return self._list_raw().data @@ -43,5 +42,5 @@ async def _list_raw(self) -> ApplicationsResponse: options=FinalRequestOptions(method="GET", url=APPLICATION_PREFIX), ) - async def list(self) -> List[ApplicationType]: + async def list(self) -> list[ApplicationType]: return (await self._list_raw()).data diff --git a/aidial_client/resources/bucket.py b/aidial_client/resources/bucket.py index c3af759..caf7536 100644 --- a/aidial_client/resources/bucket.py +++ b/aidial_client/resources/bucket.py @@ -1,4 +1,3 @@ -from typing import Optional from urllib.parse import urljoin from aidial_client._constants import API_PREFIX @@ -20,7 +19,7 @@ def get_bucket(self) -> str: response = self.get_raw() return response.bucket - def get_appdata(self) -> Optional[AppData]: + def get_appdata(self) -> AppData | None: response = self.get_raw() if not response.appdata: return None @@ -40,7 +39,7 @@ async def get_bucket(self) -> str: response = await self.get_raw() return response.bucket - async def get_appdata(self) -> Optional[AppData]: + async def get_appdata(self) -> AppData | None: response = await self.get_raw() if not response.appdata: return None diff --git a/aidial_client/resources/chat/completions.py b/aidial_client/resources/chat/completions.py index 614c8d2..5895121 100644 --- a/aidial_client/resources/chat/completions.py +++ b/aidial_client/resources/chat/completions.py @@ -1,13 +1,7 @@ +from collections.abc import AsyncIterable, Iterable, Mapping from typing import ( Any, - AsyncIterable, - Dict, - Iterable, - List, Literal, - Mapping, - Optional, - Union, cast, overload, ) @@ -42,7 +36,7 @@ class ChatCompletions(Resource): - default_api_version: Optional[str] = None + default_api_version: str | None = None openai_client: openai.AzureOpenAI @overload @@ -50,37 +44,35 @@ def create( self, *, deployment_name: str, - messages: List[Message], + messages: list[Message], stream: Literal[True], - api_version: Optional[str] = None, - model: Optional[str] = None, - functions: Union[List[FunctionParam], None] = None, - function_call: Union[ - Union[Literal["none", "auto"], FunctionCallSpecParam], None - ] = None, - tools: Union[List[ToolParam], None] = None, - tool_choice: Union[ - Union[Literal["none", "auto"], ToolCallSpecParam], None - ] = None, - addons: Union[Addon, None] = None, - temperature: Union[float, None] = None, - top_p: Union[float, None] = None, - n: Union[int, None] = None, - stop: Union[Union[str, List[str]], None] = None, - max_tokens: Union[int, None] = None, - max_prompt_tokens: Union[Union[Literal["infinity"], int], None] = None, - presence_penalty: Union[float, None] = None, - frequency_penalty: Union[float, None] = None, - logit_bias: Union[Dict, None] = None, - seed: Union[int, None] = None, - user: Union[str, None] = None, - custom_fields: Union[ChatCompletionRequestCustomFields, None] = None, - logprobs: Union[bool, None] = None, - top_logprobs: Union[int, None] = None, + api_version: str | None = None, + model: str | None = None, + functions: list[FunctionParam] | None = None, + function_call: Literal["none", "auto"] + | FunctionCallSpecParam + | None = None, + tools: list[ToolParam] | None = None, + tool_choice: Literal["none", "auto"] | ToolCallSpecParam | None = None, + addons: Addon | None = None, + temperature: float | None = None, + top_p: float | None = None, + n: int | None = None, + stop: str | list[str] | None = None, + max_tokens: int | None = None, + max_prompt_tokens: Literal["infinity"] | int | None = None, + presence_penalty: float | None = None, + frequency_penalty: float | None = None, + logit_bias: dict | None = None, + seed: int | None = None, + user: str | None = None, + custom_fields: ChatCompletionRequestCustomFields | None = None, + logprobs: bool | None = None, + top_logprobs: int | None = None, # Extra params - extra_body: Optional[Dict[str, Any]] = None, - extra_headers: Optional[Mapping[StrictStr, StrictStr]] = None, - extra_params: Optional[Dict[str, Any]] = None, + extra_body: dict[str, Any] | None = None, + extra_headers: Mapping[StrictStr, StrictStr] | None = None, + extra_params: dict[str, Any] | None = None, ) -> Iterable[ChatCompletionChunk]: ... @overload @@ -88,76 +80,71 @@ def create( self, *, deployment_name: str, - messages: List[Message], + messages: list[Message], stream: Literal[False], - api_version: Optional[str] = None, - model: Optional[str] = None, - functions: Union[List[FunctionParam], None] = None, - function_call: Union[ - Union[Literal["none", "auto"], FunctionCallSpecParam], None - ] = None, - tools: Union[List[ToolParam], None] = None, - tool_choice: Union[ - Union[Literal["none", "auto"], ToolCallSpecParam], None - ] = None, - addons: Union[Addon, None] = None, - temperature: Union[float, None] = None, - top_p: Union[float, None] = None, - n: Union[int, None] = None, - stop: Union[Union[str, List[str]], None] = None, - max_tokens: Union[int, None] = None, - max_prompt_tokens: Union[Union[Literal["infinity"], int], None] = None, - presence_penalty: Union[float, None] = None, - frequency_penalty: Union[float, None] = None, - logit_bias: Union[Dict, None] = None, - seed: Union[int, None] = None, - user: Union[str, None] = None, - custom_fields: Union[ChatCompletionRequestCustomFields, None] = None, - logprobs: Union[bool, None] = None, - top_logprobs: Union[int, None] = None, + api_version: str | None = None, + model: str | None = None, + functions: list[FunctionParam] | None = None, + function_call: Literal["none", "auto"] + | FunctionCallSpecParam + | None = None, + tools: list[ToolParam] | None = None, + tool_choice: Literal["none", "auto"] | ToolCallSpecParam | None = None, + addons: Addon | None = None, + temperature: float | None = None, + top_p: float | None = None, + n: int | None = None, + stop: str | list[str] | None = None, + max_tokens: int | None = None, + max_prompt_tokens: Literal["infinity"] | int | None = None, + presence_penalty: float | None = None, + frequency_penalty: float | None = None, + logit_bias: dict | None = None, + seed: int | None = None, + user: str | None = None, + custom_fields: ChatCompletionRequestCustomFields | None = None, + logprobs: bool | None = None, + top_logprobs: int | None = None, # Extra params - extra_body: Optional[Dict[str, Any]] = None, - extra_headers: Optional[Mapping[StrictStr, StrictStr]] = None, - extra_params: Optional[Dict[str, Any]] = None, + extra_body: dict[str, Any] | None = None, + extra_headers: Mapping[StrictStr, StrictStr] | None = None, + extra_params: dict[str, Any] | None = None, ) -> ChatCompletionResponse: ... def create( self, *, deployment_name: str, - messages: List[Message], - api_version: Optional[str] = None, + messages: list[Message], + api_version: str | None = None, stream: bool = False, - model: Optional[str] = None, - functions: Union[List[FunctionParam], None] = None, - function_call: Union[ - Union[Literal["none", "auto"], FunctionCallSpecParam], None - ] = None, - tools: Union[List[ToolParam], None] = None, - tool_choice: Union[ - Union[Literal["none", "auto"], ToolCallSpecParam], None - ] = None, - addons: Union[Addon, None] = None, - temperature: Union[float, None] = None, - top_p: Union[float, None] = None, - n: Union[int, None] = None, - stop: Union[Union[str, List[str]], None] = None, - max_tokens: Union[int, None] = None, - max_prompt_tokens: Union[Union[Literal["infinity"], int], None] = None, - presence_penalty: Union[float, None] = None, - frequency_penalty: Union[float, None] = None, - logit_bias: Union[Dict, None] = None, - seed: Union[int, None] = None, - user: Union[str, None] = None, - custom_fields: Union[ChatCompletionRequestCustomFields, None] = None, - logprobs: Union[bool, None] = None, - top_logprobs: Union[int, None] = None, + model: str | None = None, + functions: list[FunctionParam] | None = None, + function_call: Literal["none", "auto"] + | FunctionCallSpecParam + | None = None, + tools: list[ToolParam] | None = None, + tool_choice: Literal["none", "auto"] | ToolCallSpecParam | None = None, + addons: Addon | None = None, + temperature: float | None = None, + top_p: float | None = None, + n: int | None = None, + stop: str | list[str] | None = None, + max_tokens: int | None = None, + max_prompt_tokens: Literal["infinity"] | int | None = None, + presence_penalty: float | None = None, + frequency_penalty: float | None = None, + logit_bias: dict | None = None, + seed: int | None = None, + user: str | None = None, + custom_fields: ChatCompletionRequestCustomFields | None = None, + logprobs: bool | None = None, + top_logprobs: int | None = None, # Extra params - extra_body: Optional[Dict[str, Any]] = None, - extra_headers: Optional[Mapping[StrictStr, StrictStr]] = None, - extra_params: Optional[Dict[str, Any]] = None, - ) -> Union[ChatCompletionResponse, Iterable[ChatCompletionChunk]]: - + extra_body: dict[str, Any] | None = None, + extra_headers: Mapping[StrictStr, StrictStr] | None = None, + extra_params: dict[str, Any] | None = None, + ) -> ChatCompletionResponse | Iterable[ChatCompletionChunk]: model = model or deployment_name extra_body = extra_body or {} extra_headers = extra_headers or {} @@ -206,10 +193,7 @@ def create( **input_params, ) openai_response = cast( - Union[ - OpenaiChatCompletion, - OpenaiStream[OpenaiChatCompletionChunk], - ], + OpenaiChatCompletion | OpenaiStream[OpenaiChatCompletionChunk], openai_response, ) except openai.APIError as err: @@ -222,7 +206,7 @@ def create( class AsyncChatCompletions(AsyncResource): - default_api_version: Optional[str] = None + default_api_version: str | None = None openai_client: openai.AsyncAzureOpenAI @overload @@ -230,35 +214,33 @@ async def create( self, *, deployment_name: str, - messages: List[Message], + messages: list[Message], stream: Literal[True], - api_version: Optional[str] = None, - model: Optional[str] = None, - functions: Union[List[FunctionParam], None] = None, - function_call: Union[ - Union[Literal["none", "auto"], FunctionCallSpecParam], None - ] = None, - tools: Union[List[ToolParam], None] = None, - tool_choice: Union[ - Union[Literal["none", "auto"], ToolCallSpecParam], None - ] = None, - addons: Union[Addon, None] = None, - temperature: Union[float, None] = None, - top_p: Union[float, None] = None, - n: Union[int, None] = None, - stop: Union[Union[str, List[str]], None] = None, - max_tokens: Union[int, None] = None, - max_prompt_tokens: Union[Union[Literal["infinity"], int], None] = None, - presence_penalty: Union[float, None] = None, - frequency_penalty: Union[float, None] = None, - logit_bias: Union[Dict, None] = None, - seed: Union[int, None] = None, - user: Union[str, None] = None, - custom_fields: Union[ChatCompletionRequestCustomFields, None] = None, + api_version: str | None = None, + model: str | None = None, + functions: list[FunctionParam] | None = None, + function_call: Literal["none", "auto"] + | FunctionCallSpecParam + | None = None, + tools: list[ToolParam] | None = None, + tool_choice: Literal["none", "auto"] | ToolCallSpecParam | None = None, + addons: Addon | None = None, + temperature: float | None = None, + top_p: float | None = None, + n: int | None = None, + stop: str | list[str] | None = None, + max_tokens: int | None = None, + max_prompt_tokens: Literal["infinity"] | int | None = None, + presence_penalty: float | None = None, + frequency_penalty: float | None = None, + logit_bias: dict | None = None, + seed: int | None = None, + user: str | None = None, + custom_fields: ChatCompletionRequestCustomFields | None = None, # Extra params - extra_body: Optional[Dict[str, Any]] = None, - extra_headers: Optional[Mapping[StrictStr, StrictStr]] = None, - extra_params: Optional[Dict[str, Any]] = None, + extra_body: dict[str, Any] | None = None, + extra_headers: Mapping[StrictStr, StrictStr] | None = None, + extra_params: dict[str, Any] | None = None, ) -> AsyncIterable[ChatCompletionChunk]: ... @overload @@ -266,75 +248,71 @@ async def create( self, *, deployment_name: str, - messages: List[Message], + messages: list[Message], stream: Literal[False], - api_version: Optional[str] = None, - model: Optional[str] = None, - functions: Union[List[FunctionParam], None] = None, - function_call: Union[ - Union[Literal["none", "auto"], FunctionCallSpecParam], None - ] = None, - tools: Union[List[ToolParam], None] = None, - tool_choice: Union[ - Union[Literal["none", "auto"], ToolCallSpecParam], None - ] = None, - addons: Union[Addon, None] = None, - temperature: Union[float, None] = None, - top_p: Union[float, None] = None, - n: Union[int, None] = None, - stop: Union[Union[str, List[str]], None] = None, - max_tokens: Union[int, None] = None, - max_prompt_tokens: Union[Union[Literal["infinity"], int], None] = None, - presence_penalty: Union[float, None] = None, - frequency_penalty: Union[float, None] = None, - logit_bias: Union[Dict, None] = None, - seed: Union[int, None] = None, - user: Union[str, None] = None, - custom_fields: Union[ChatCompletionRequestCustomFields, None] = None, - logprobs: Union[bool, None] = None, - top_logprobs: Union[int, None] = None, + api_version: str | None = None, + model: str | None = None, + functions: list[FunctionParam] | None = None, + function_call: Literal["none", "auto"] + | FunctionCallSpecParam + | None = None, + tools: list[ToolParam] | None = None, + tool_choice: Literal["none", "auto"] | ToolCallSpecParam | None = None, + addons: Addon | None = None, + temperature: float | None = None, + top_p: float | None = None, + n: int | None = None, + stop: str | list[str] | None = None, + max_tokens: int | None = None, + max_prompt_tokens: Literal["infinity"] | int | None = None, + presence_penalty: float | None = None, + frequency_penalty: float | None = None, + logit_bias: dict | None = None, + seed: int | None = None, + user: str | None = None, + custom_fields: ChatCompletionRequestCustomFields | None = None, + logprobs: bool | None = None, + top_logprobs: int | None = None, # Extra params - extra_body: Optional[Dict[str, Any]] = None, - extra_headers: Optional[Mapping[StrictStr, StrictStr]] = None, - extra_params: Optional[Dict[str, Any]] = None, + extra_body: dict[str, Any] | None = None, + extra_headers: Mapping[StrictStr, StrictStr] | None = None, + extra_params: dict[str, Any] | None = None, ) -> ChatCompletionResponse: ... async def create( self, *, deployment_name: str, - messages: List[Message], - api_version: Optional[str] = None, + messages: list[Message], + api_version: str | None = None, stream: bool = False, - model: Optional[str] = None, - functions: Union[List[FunctionParam], None] = None, - function_call: Union[ - Union[Literal["none", "auto"], FunctionCallSpecParam], None - ] = None, - tools: Union[List[ToolParam], None] = None, - tool_choice: Union[ - Union[Literal["none", "auto"], ToolCallSpecParam], None - ] = None, - addons: Union[Addon, None] = None, - temperature: Union[float, None] = None, - top_p: Union[float, None] = None, - n: Union[int, None] = None, - stop: Union[Union[str, List[str]], None] = None, - max_tokens: Union[int, None] = None, - max_prompt_tokens: Union[Union[Literal["infinity"], int], None] = None, - presence_penalty: Union[float, None] = None, - frequency_penalty: Union[float, None] = None, - logit_bias: Union[Dict, None] = None, - seed: Union[int, None] = None, - user: Union[str, None] = None, - custom_fields: Union[ChatCompletionRequestCustomFields, None] = None, - logprobs: Union[bool, None] = None, - top_logprobs: Union[int, None] = None, + model: str | None = None, + functions: list[FunctionParam] | None = None, + function_call: Literal["none", "auto"] + | FunctionCallSpecParam + | None = None, + tools: list[ToolParam] | None = None, + tool_choice: Literal["none", "auto"] | ToolCallSpecParam | None = None, + addons: Addon | None = None, + temperature: float | None = None, + top_p: float | None = None, + n: int | None = None, + stop: str | list[str] | None = None, + max_tokens: int | None = None, + max_prompt_tokens: Literal["infinity"] | int | None = None, + presence_penalty: float | None = None, + frequency_penalty: float | None = None, + logit_bias: dict | None = None, + seed: int | None = None, + user: str | None = None, + custom_fields: ChatCompletionRequestCustomFields | None = None, + logprobs: bool | None = None, + top_logprobs: int | None = None, # Extra params - extra_body: Optional[Dict[str, Any]] = None, - extra_headers: Optional[Mapping[StrictStr, StrictStr]] = None, - extra_params: Optional[Dict[str, Any]] = None, - ) -> Union[ChatCompletionResponse, AsyncIterable[ChatCompletionChunk]]: + extra_body: dict[str, Any] | None = None, + extra_headers: Mapping[StrictStr, StrictStr] | None = None, + extra_params: dict[str, Any] | None = None, + ) -> ChatCompletionResponse | AsyncIterable[ChatCompletionChunk]: model = model or deployment_name extra_body = extra_body or {} extra_headers = extra_headers or {} @@ -383,10 +361,8 @@ async def create( **input_params, ) openai_response = cast( - Union[ - OpenaiChatCompletion, - OpenaiAsyncStream[OpenaiChatCompletionChunk], - ], + OpenaiChatCompletion + | OpenaiAsyncStream[OpenaiChatCompletionChunk], openai_response, ) except openai.APIError as err: diff --git a/aidial_client/resources/client_channel.py b/aidial_client/resources/client_channel.py index 9135262..c701a76 100644 --- a/aidial_client/resources/client_channel.py +++ b/aidial_client/resources/client_channel.py @@ -1,5 +1,6 @@ +from collections.abc import Sequence from http import HTTPStatus -from typing import Any, List, Optional, Sequence, Union +from typing import Any import httpx @@ -24,7 +25,7 @@ _SIGNIN_METHOD = "toolset/signin" -def _normalize_toolset_ids(toolset_ids: Sequence[str]) -> List[str]: +def _normalize_toolset_ids(toolset_ids: Sequence[str]) -> list[str]: """Validate ``toolset_ids`` and return a stable list. Catches three caller mistakes that would otherwise produce silent garbage: @@ -53,14 +54,13 @@ def _serialize_requests(requests: Sequence[JsonRpcRequest]) -> Any: return [r.dict(exclude_none=True) for r in requests] -def _parse_responses(payload: str) -> List[JsonRpcResponse]: +def _parse_responses(payload: str) -> list[JsonRpcResponse]: try: return JsonRpcResponses.parse_raw(payload).responses except (ValidationError, ValueError) as err: raise ParsingDataError( message=( - "Invalid JSON-RPC response in client-channel interact: " - f"{err}" + f"Invalid JSON-RPC response in client-channel interact: {err}" ) ) from err @@ -95,7 +95,7 @@ def _raise_if_batch_error(responses: Sequence[JsonRpcResponse]) -> None: } -def _outcome_for(response: Optional[JsonRpcResponse]) -> SigninResult: +def _outcome_for(response: JsonRpcResponse | None) -> SigninResult: if response is None or response.error is not None: return SigninResult.ERROR if not isinstance(response.result, str): @@ -105,7 +105,7 @@ def _outcome_for(response: Optional[JsonRpcResponse]) -> SigninResult: def _build_signin_requests( toolset_ids: Sequence[str], -) -> List[JsonRpcRequest]: +) -> list[JsonRpcRequest]: return [ JsonRpcRequest( method=_SIGNIN_METHOD, @@ -133,7 +133,7 @@ def signin_toolsets( *, channel_id: str, toolset_ids: Sequence[str], - timeout: Union[float, httpx.Timeout, None, NotGiven] = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> "dict[str, SigninResult]": """Request interactive sign-in for one or more toolsets on the given client channel and return the per-toolset outcome. @@ -166,8 +166,8 @@ def _interact( *, channel_id: str, requests: Sequence[JsonRpcRequest], - timeout: Union[float, httpx.Timeout, None, NotGiven] = NOT_GIVEN, - ) -> List[JsonRpcResponse]: + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> list[JsonRpcResponse]: with self.http_client.stream_sse( method="POST", url=_INTERACT_URL, @@ -186,7 +186,7 @@ async def signin_toolsets( *, channel_id: str, toolset_ids: Sequence[str], - timeout: Union[float, httpx.Timeout, None, NotGiven] = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> "dict[str, SigninResult]": ids = _normalize_toolset_ids(toolset_ids) if not ids: @@ -204,8 +204,8 @@ async def _interact( *, channel_id: str, requests: Sequence[JsonRpcRequest], - timeout: Union[float, httpx.Timeout, None, NotGiven] = NOT_GIVEN, - ) -> List[JsonRpcResponse]: + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> list[JsonRpcResponse]: async with self.http_client.stream_sse( method="POST", url=_INTERACT_URL, diff --git a/aidial_client/resources/deployments.py b/aidial_client/resources/deployments.py index 10e4fb5..2185be4 100644 --- a/aidial_client/resources/deployments.py +++ b/aidial_client/resources/deployments.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List +from typing import Any from aidial_client._internal_types._http_request import FinalRequestOptions from aidial_client.resources.base import AsyncResource, Resource @@ -12,7 +12,7 @@ def _list_raw(self) -> DeploymentsResponse: options=FinalRequestOptions(method="GET", url="openai/deployments"), ) - def list(self) -> List[Deployment]: + def list(self) -> list[Deployment]: return self._list_raw().data def get(self, deployment_id: str) -> Deployment: @@ -23,7 +23,7 @@ def get(self, deployment_id: str) -> Deployment: ), ) - def get_configuration_schema(self, deployment_id: str) -> Dict[str, Any]: + def get_configuration_schema(self, deployment_id: str) -> dict[str, Any]: return self.http_client.request( cast_to=dict, options=FinalRequestOptions( @@ -40,7 +40,7 @@ async def _list_raw(self) -> DeploymentsResponse: options=FinalRequestOptions(method="GET", url="openai/deployments"), ) - async def list(self) -> List[Deployment]: + async def list(self) -> list[Deployment]: return (await self._list_raw()).data async def get(self, deployment_id: str) -> Deployment: @@ -53,7 +53,7 @@ async def get(self, deployment_id: str) -> Deployment: async def get_configuration_schema( self, deployment_id: str - ) -> Dict[str, Any]: + ) -> dict[str, Any]: return await self.http_client.request( cast_to=dict, options=FinalRequestOptions( diff --git a/aidial_client/resources/files.py b/aidial_client/resources/files.py index 47421aa..f7a2957 100644 --- a/aidial_client/resources/files.py +++ b/aidial_client/resources/files.py @@ -1,5 +1,5 @@ from pathlib import PurePosixPath -from typing import Literal, Optional, Union +from typing import Literal from urllib.parse import urljoin import httpx @@ -26,7 +26,7 @@ def _files_error_processor( http_status_error: httpx.HTTPStatusError, -) -> Optional[DialException]: +) -> DialException | None: if http_status_error.response.status_code == 412: return EtagMismatchError( message=http_status_error.response.text, @@ -44,10 +44,10 @@ class Files(Resource, DialStorageResourceMixin): def upload( self, - url: Union[str, PurePosixPath], + url: str | PurePosixPath, file: FileTypes, - etag_if_match: Optional[str] = None, - etag_if_none_match: Optional[Literal["*"]] = None, + etag_if_match: str | None = None, + etag_if_none_match: Literal["*"] | None = None, ) -> FileMetadata: return self.http_client.request( cast_to=FileMetadata, @@ -67,8 +67,8 @@ def upload( def download( self, - url: Union[str, PurePosixPath], - etag_if_match: Optional[str] = None, + url: str | PurePosixPath, + etag_if_match: str | None = None, ) -> FileDownloadResponse: storage_resource = self.get_storage_resource(str(url)) if storage_resource.filename is None: @@ -92,8 +92,8 @@ def download( def delete( self, - url: Union[str, PurePosixPath], - etag_if_match: Optional[str] = None, + url: str | PurePosixPath, + etag_if_match: str | None = None, ) -> None: return self.http_client.request( cast_to=NoneType, @@ -111,8 +111,8 @@ def delete( def move_to( self, - source: Union[str, PurePosixPath], - destination: Union[str, PurePosixPath], + source: str | PurePosixPath, + destination: str | PurePosixPath, overwrite: bool = False, ) -> None: return self.http_client.request( @@ -131,8 +131,8 @@ def move_to( def copy_to( self, - source: Union[str, PurePosixPath], - destination: Union[str, PurePosixPath], + source: str | PurePosixPath, + destination: str | PurePosixPath, overwrite: bool = False, ) -> None: return self.http_client.request( @@ -149,7 +149,7 @@ def copy_to( on_http_error=_files_error_processor, ) - def get_metadata(self, url: Union[str, PurePosixPath]) -> FileMetadata: + def get_metadata(self, url: str | PurePosixPath) -> FileMetadata: return self.metadata.get( resource="files", relative_url=self.get_api_path(str(url)), @@ -162,12 +162,11 @@ class AsyncFiles(AsyncResource, DialStorageResourceMixin): async def upload( self, - url: Union[str, PurePosixPath], + url: str | PurePosixPath, file: FileTypes, - etag_if_match: Optional[str] = None, - etag_if_none_match: Optional[Literal["*"]] = None, + etag_if_match: str | None = None, + etag_if_none_match: Literal["*"] | None = None, ) -> FileMetadata: - return await self.http_client.request( cast_to=FileMetadata, options=FinalRequestOptions( @@ -186,8 +185,8 @@ async def upload( async def download( self, - url: Union[str, PurePosixPath], - etag_if_match: Optional[str] = None, + url: str | PurePosixPath, + etag_if_match: str | None = None, ) -> FileDownloadResponse: storage_resource = self.get_storage_resource(str(url)) if storage_resource.filename is None: @@ -211,8 +210,8 @@ async def download( async def delete( self, - url: Union[str, PurePosixPath], - etag_if_match: Optional[str] = None, + url: str | PurePosixPath, + etag_if_match: str | None = None, ) -> None: return await self.http_client.request( cast_to=NoneType, @@ -230,8 +229,8 @@ async def delete( async def move_to( self, - source: Union[str, PurePosixPath], - destination: Union[str, PurePosixPath], + source: str | PurePosixPath, + destination: str | PurePosixPath, overwrite: bool = False, ) -> None: return await self.http_client.request( @@ -250,8 +249,8 @@ async def move_to( async def copy_to( self, - source: Union[str, PurePosixPath], - destination: Union[str, PurePosixPath], + source: str | PurePosixPath, + destination: str | PurePosixPath, overwrite: bool = False, ) -> None: return await self.http_client.request( @@ -268,9 +267,7 @@ async def copy_to( on_http_error=_files_error_processor, ) - async def get_metadata( - self, url: Union[str, PurePosixPath] - ) -> FileMetadata: + async def get_metadata(self, url: str | PurePosixPath) -> FileMetadata: return await self.metadata.get( resource="files", relative_url=self.get_api_path(str(url)), diff --git a/aidial_client/resources/metadata.py b/aidial_client/resources/metadata.py index d970935..e13d5c2 100644 --- a/aidial_client/resources/metadata.py +++ b/aidial_client/resources/metadata.py @@ -1,4 +1,4 @@ -from typing import Literal, Type, Union, overload +from typing import Literal, overload from urllib.parse import urljoin from typing_extensions import assert_never @@ -16,9 +16,7 @@ def _get_cast_to( resource: StorageResourceType, -) -> Union[ - Type[FileMetadata], Type[ConversationMetadata], Type[PromptMetadata] -]: +) -> type[FileMetadata] | type[ConversationMetadata] | type[PromptMetadata]: if resource == "files": return FileMetadata elif resource == "conversations": @@ -49,7 +47,7 @@ def get( self, resource: StorageResourceType, relative_url: str, - ) -> Union[FileMetadata, ConversationMetadata, PromptMetadata]: + ) -> FileMetadata | ConversationMetadata | PromptMetadata: return self.http_client.request( cast_to=_get_cast_to(resource), options=FinalRequestOptions( @@ -79,7 +77,7 @@ async def get( self, resource: StorageResourceType, relative_url: str, - ) -> Union[FileMetadata, ConversationMetadata, PromptMetadata]: + ) -> FileMetadata | ConversationMetadata | PromptMetadata: return await self.http_client.request( cast_to=_get_cast_to(resource), options=FinalRequestOptions( diff --git a/aidial_client/resources/prompts.py b/aidial_client/resources/prompts.py index 9690d08..f4088f9 100644 --- a/aidial_client/resources/prompts.py +++ b/aidial_client/resources/prompts.py @@ -1,5 +1,5 @@ from pathlib import PurePosixPath -from typing import Any, Dict, Literal, Optional, Union +from typing import Any, Literal from urllib.parse import urljoin import httpx @@ -23,7 +23,7 @@ def _prompts_error_processor( http_status_error: httpx.HTTPStatusError, -) -> Optional[DialException]: +) -> DialException | None: if http_status_error.response.status_code == 412: return EtagMismatchError( message=http_status_error.response.text, @@ -35,7 +35,7 @@ def _prompts_error_processor( return None -def _prompt_to_json(prompt: Prompt) -> Dict[str, Any]: +def _prompt_to_json(prompt: Prompt) -> dict[str, Any]: if PYDANTIC_V2: return prompt.model_dump(by_alias=True) # type: ignore return prompt.dict(by_alias=True) @@ -47,10 +47,10 @@ class Prompts(Resource, DialStorageResourceMixin): def save( self, - url: Union[str, PurePosixPath], + url: str | PurePosixPath, prompt: Prompt, - etag_if_match: Optional[str] = None, - etag_if_none_match: Optional[Literal["*"]] = None, + etag_if_match: str | None = None, + etag_if_none_match: Literal["*"] | None = None, ) -> PromptMetadata: return self.http_client.request( cast_to=PromptMetadata, @@ -68,7 +68,7 @@ def save( on_http_error=_prompts_error_processor, ) - def get(self, url: Union[str, PurePosixPath]) -> Prompt: + def get(self, url: str | PurePosixPath) -> Prompt: """Fetch a single prompt by its storage path.""" return self.http_client.request( cast_to=Prompt, @@ -81,8 +81,8 @@ def get(self, url: Union[str, PurePosixPath]) -> Prompt: def delete( self, - url: Union[str, PurePosixPath], - etag_if_match: Optional[str] = None, + url: str | PurePosixPath, + etag_if_match: str | None = None, ) -> None: return self.http_client.request( cast_to=NoneType, @@ -98,7 +98,7 @@ def delete( on_http_error=_prompts_error_processor, ) - def get_metadata(self, url: Union[str, PurePosixPath]) -> PromptMetadata: + def get_metadata(self, url: str | PurePosixPath) -> PromptMetadata: return self.metadata.get( resource="prompts", relative_url=self.get_api_path(str(url)), @@ -111,10 +111,10 @@ class AsyncPrompts(AsyncResource, DialStorageResourceMixin): async def save( self, - url: Union[str, PurePosixPath], + url: str | PurePosixPath, prompt: Prompt, - etag_if_match: Optional[str] = None, - etag_if_none_match: Optional[Literal["*"]] = None, + etag_if_match: str | None = None, + etag_if_none_match: Literal["*"] | None = None, ) -> PromptMetadata: return await self.http_client.request( cast_to=PromptMetadata, @@ -132,7 +132,7 @@ async def save( on_http_error=_prompts_error_processor, ) - async def get(self, url: Union[str, PurePosixPath]) -> Prompt: + async def get(self, url: str | PurePosixPath) -> Prompt: """Fetch a single prompt by its storage path.""" return await self.http_client.request( cast_to=Prompt, @@ -145,8 +145,8 @@ async def get(self, url: Union[str, PurePosixPath]) -> Prompt: async def delete( self, - url: Union[str, PurePosixPath], - etag_if_match: Optional[str] = None, + url: str | PurePosixPath, + etag_if_match: str | None = None, ) -> None: return await self.http_client.request( cast_to=NoneType, @@ -162,9 +162,7 @@ async def delete( on_http_error=_prompts_error_processor, ) - async def get_metadata( - self, url: Union[str, PurePosixPath] - ) -> PromptMetadata: + async def get_metadata(self, url: str | PurePosixPath) -> PromptMetadata: return await self.metadata.get( resource="prompts", relative_url=self.get_api_path(str(url)), diff --git a/aidial_client/resources/resource_permissions.py b/aidial_client/resources/resource_permissions.py index d05ed05..494aa41 100644 --- a/aidial_client/resources/resource_permissions.py +++ b/aidial_client/resources/resource_permissions.py @@ -1,5 +1,3 @@ -from typing import List - from aidial_client._internal_types._generic import NoneType from aidial_client._internal_types._http_request import FinalRequestOptions from aidial_client.resources.base import AsyncResource, Resource @@ -10,10 +8,12 @@ class ResourcePermissions(Resource): def grant( self, - resources: List[str], + resources: list[str], receiver: str, - permissions: List[str] = ["READ"], + permissions: list[str] | None = None, ) -> None: + if permissions is None: + permissions = ["READ"] self.http_client.request( cast_to=NoneType, options=FinalRequestOptions( @@ -33,10 +33,12 @@ def grant( class AsyncResourcePermissions(AsyncResource): async def grant( self, - resources: List[str], + resources: list[str], receiver: str, - permissions: List[str] = ["READ"], + permissions: list[str] | None = None, ) -> None: + if permissions is None: + permissions = ["READ"] await self.http_client.request( cast_to=NoneType, options=FinalRequestOptions( diff --git a/aidial_client/types/application.py b/aidial_client/types/application.py index eb6a8af..37e24ee 100644 --- a/aidial_client/types/application.py +++ b/aidial_client/types/application.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Literal, Optional +from typing import Literal from aidial_client._internal_types._model import ExtraAllowModel from aidial_client.types.deployment import DeploymentBase @@ -7,11 +7,11 @@ class Application(DeploymentBase): object: Literal["application"] application: str - application_type_schema_id: Optional[str] = None - application_properties: Optional[Dict] = None - invalid: Optional[bool] = None + application_type_schema_id: str | None = None + application_properties: dict | None = None + invalid: bool | None = None class ApplicationsResponse(ExtraAllowModel): - data: List[Application] + data: list[Application] object: Literal["list"] diff --git a/aidial_client/types/bucket.py b/aidial_client/types/bucket.py index 1ca2063..6c0cb17 100644 --- a/aidial_client/types/bucket.py +++ b/aidial_client/types/bucket.py @@ -1,5 +1,4 @@ import re -from typing import Optional from aidial_client._internal_types._model import ExtraAllowModel @@ -21,4 +20,4 @@ def parse(cls, appdata: str) -> "AppData": class BucketResponse(ExtraAllowModel): bucket: str - appdata: Optional[str] = None + appdata: str | None = None diff --git a/aidial_client/types/chat/addon.py b/aidial_client/types/chat/addon.py index d59334b..7d1e290 100644 --- a/aidial_client/types/chat/addon.py +++ b/aidial_client/types/chat/addon.py @@ -1,5 +1,3 @@ -from typing import Union - from typing_extensions import TypedDict @@ -14,4 +12,4 @@ class SystemAddon(TypedDict): name: str -Addon = Union[ExternalAddon, SystemAddon] +Addon = ExternalAddon | SystemAddon diff --git a/aidial_client/types/chat/function.py b/aidial_client/types/chat/function.py index 41b7e35..c38e453 100644 --- a/aidial_client/types/chat/function.py +++ b/aidial_client/types/chat/function.py @@ -1,12 +1,10 @@ -from typing import Dict, Optional - from typing_extensions import Required, TypedDict class FunctionParam(TypedDict, total=False): name: Required[str] - description: Optional[str] - parameters: Optional[Dict] + description: str | None + parameters: dict | None class FunctionCallParam(TypedDict): diff --git a/aidial_client/types/chat/legacy/application_request.py b/aidial_client/types/chat/legacy/application_request.py index c9449d3..65bb7eb 100644 --- a/aidial_client/types/chat/legacy/application_request.py +++ b/aidial_client/types/chat/legacy/application_request.py @@ -1,4 +1,4 @@ -from typing import Dict, Mapping, Optional +from collections.abc import Mapping from aidial_client._auth import get_combined_auth_headers from aidial_client._compatibility.pydantic_v1 import ( @@ -14,10 +14,10 @@ class RequestParams(ExtraForbidModel): api_key_secret: SecretStr - jwt_secret: Optional[SecretStr] = None + jwt_secret: SecretStr | None = None deployment_id: StrictStr - api_version: Optional[StrictStr] = None + api_version: StrictStr | None = None headers: Mapping[StrictStr, StrictStr] @root_validator(pre=True) @@ -43,11 +43,11 @@ def api_key(self) -> str: return self.api_key_secret.get_secret_value() @property - def jwt(self) -> Optional[str]: + def jwt(self) -> str | None: return self.jwt_secret.get_secret_value() if self.jwt_secret else None @property - def auth_headers(self) -> Dict[str, str]: + def auth_headers(self) -> dict[str, str]: if self.jwt_secret is not None: return get_combined_auth_headers( bearer_token=self.jwt_secret.get_secret_value(), diff --git a/aidial_client/types/chat/legacy/chat_completion.py b/aidial_client/types/chat/legacy/chat_completion.py index d91b7ec..bc12b9e 100644 --- a/aidial_client/types/chat/legacy/chat_completion.py +++ b/aidial_client/types/chat/legacy/chat_completion.py @@ -1,5 +1,6 @@ +from collections.abc import Mapping from enum import Enum -from typing import Any, Dict, List, Literal, Mapping, Optional, Union +from typing import Any, Literal from aidial_client._compatibility.pydantic_v1 import ( ConstrainedFloat, @@ -27,25 +28,25 @@ class Status(str, Enum): class Attachment(ExtraForbidModel): - type: Optional[StrictStr] = "text/markdown" - title: Optional[StrictStr] = None - data: Optional[StrictStr] = None - url: Optional[StrictStr] = None - reference_type: Optional[StrictStr] = None - reference_url: Optional[StrictStr] = None + type: StrictStr | None = "text/markdown" + title: StrictStr | None = None + data: StrictStr | None = None + url: StrictStr | None = None + reference_type: StrictStr | None = None + reference_url: StrictStr | None = None class Stage(ExtraForbidModel): name: StrictStr status: Status - content: Optional[StrictStr] = None - attachments: Optional[List[Attachment]] = None + content: StrictStr | None = None + attachments: list[Attachment] | None = None class CustomContent(ExtraForbidModel): - stages: Optional[List[Stage]] = None - attachments: Optional[List[Attachment]] = None - state: Optional[Any] = None + stages: list[Stage] | None = None + attachments: list[Attachment] | None = None + state: Any | None = None class FunctionCall(ExtraForbidModel): @@ -55,7 +56,7 @@ class FunctionCall(ExtraForbidModel): class ToolCall(ExtraForbidModel): # OpenAI API doesn't strictly specify existence of the index field - index: Optional[int] + index: int | None id: StrictStr type: Literal["function"] function: FunctionCall @@ -71,23 +72,23 @@ class Role(str, Enum): class Message(ExtraForbidModel): role: Role - content: Optional[StrictStr] = None - custom_content: Optional[CustomContent] = None - name: Optional[StrictStr] = None - tool_calls: Optional[List[ToolCall]] = None - tool_call_id: Optional[StrictStr] = None - function_call: Optional[FunctionCall] = None + content: StrictStr | None = None + custom_content: CustomContent | None = None + name: StrictStr | None = None + tool_calls: list[ToolCall] | None = None + tool_call_id: StrictStr | None = None + function_call: FunctionCall | None = None class Addon(ExtraForbidModel): - name: Optional[StrictStr] = None - url: Optional[StrictStr] = None + name: StrictStr | None = None + url: StrictStr | None = None class Function(ExtraForbidModel): name: StrictStr - description: Optional[StrictStr] = None - parameters: Optional[Dict] = None + description: StrictStr | None = None + parameters: dict | None = None class Temperature(ConstrainedFloat): @@ -107,7 +108,7 @@ class N(ConstrainedInt): class Stop(ConstrainedList): max_items: int = 4 - __args__ = tuple([StrictStr]) + __args__ = (StrictStr,) class Penalty(ConstrainedFloat): @@ -134,35 +135,33 @@ class ResponseFormat(ExtraForbidModel): class AzureChatCompletionRequest(ExtraForbidModel): - model: Optional[StrictStr] = None - messages: List[Message] - functions: Optional[List[Function]] = None - function_call: Optional[Union[Literal["auto", "none"], FunctionChoice]] = ( - None - ) - tools: Optional[List[Tool]] = None - tool_choice: Optional[Union[Literal["auto", "none"], ToolChoice]] = None + model: StrictStr | None = None + messages: list[Message] + functions: list[Function] | None = None + function_call: Literal["auto", "none"] | FunctionChoice | None = None + tools: list[Tool] | None = None + tool_choice: Literal["auto", "none"] | ToolChoice | None = None stream: bool = False - temperature: Optional[Temperature] = None - top_p: Optional[TopP] = None - n: Optional[N] = None - stop: Optional[Union[StrictStr, Stop]] = None - max_tokens: Optional[PositiveInt] = None - presence_penalty: Optional[Penalty] = None - frequency_penalty: Optional[Penalty] = None - logit_bias: Optional[Mapping[int, float]] = None - user: Optional[StrictStr] = None - seed: Optional[StrictInt] = None - logprobs: Optional[StrictBool] = None - top_logprobs: Optional[StrictInt] = None - response_format: Optional[ResponseFormat] = None + temperature: Temperature | None = None + top_p: TopP | None = None + n: N | None = None + stop: StrictStr | Stop | None = None + max_tokens: PositiveInt | None = None + presence_penalty: Penalty | None = None + frequency_penalty: Penalty | None = None + logit_bias: Mapping[int, float] | None = None + user: StrictStr | None = None + seed: StrictInt | None = None + logprobs: StrictBool | None = None + top_logprobs: StrictInt | None = None + response_format: ResponseFormat | None = None class ChatCompletionRequestCustomFields(ExtraForbidModel): - configuration: Optional[Dict[str, Any]] = None + configuration: dict[str, Any] | None = None class ChatCompletionRequest(AzureChatCompletionRequest): - addons: Optional[List[Addon]] = None - max_prompt_tokens: Optional[PositiveInt] = None - custom_fields: Optional[ChatCompletionRequestCustomFields] = None + addons: list[Addon] | None = None + max_prompt_tokens: PositiveInt | None = None + custom_fields: ChatCompletionRequestCustomFields | None = None diff --git a/aidial_client/types/chat/request.py b/aidial_client/types/chat/request.py index 4820f88..d8bc743 100644 --- a/aidial_client/types/chat/request.py +++ b/aidial_client/types/chat/request.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Literal, Optional, Union +from typing import Any, Literal from typing_extensions import TypedDict @@ -12,33 +12,31 @@ class ChatCompletionRequestCustomFields(TypedDict, total=False): - configuration: Optional[Dict[str, Any]] + configuration: dict[str, Any] | None class ChatCompletionRequest(TypedDict, total=False): model: str - temperature: Optional[float] - top_p: Optional[float] - stream: Optional[bool] - stop: Optional[Union[str, List[str]]] - max_tokens: Optional[int] - presence_penalty: Optional[float] - frequency_penalty: Optional[float] - logit_bias: Optional[Dict] - user: Optional[str] - messages: List[Message] - data_sources: List[Any] - n: Optional[int] - seed: Optional[int] - logprobs: Optional[bool] - top_logprobs: Optional[float] - response_format: Optional[ResponseFormat] - tools: Optional[List[ToolParam]] - tool_choice: Optional[Union[Literal["none", "auto"], ToolCallSpecParam]] - functions: Optional[List[FunctionParam]] - function_call: Optional[ - Union[Literal["none", "auto"], FunctionCallSpecParam] - ] - addons: Optional[Addon] - max_prompt_tokens: Optional[Union[Literal["infinity"], int]] - custom_fields: Optional[ChatCompletionRequestCustomFields] + temperature: float | None + top_p: float | None + stream: bool | None + stop: str | list[str] | None + max_tokens: int | None + presence_penalty: float | None + frequency_penalty: float | None + logit_bias: dict | None + user: str | None + messages: list[Message] + data_sources: list[Any] + n: int | None + seed: int | None + logprobs: bool | None + top_logprobs: float | None + response_format: ResponseFormat | None + tools: list[ToolParam] | None + tool_choice: Literal["none", "auto"] | ToolCallSpecParam | None + functions: list[FunctionParam] | None + function_call: Literal["none", "auto"] | FunctionCallSpecParam | None + addons: Addon | None + max_prompt_tokens: Literal["infinity"] | int | None + custom_fields: ChatCompletionRequestCustomFields | None diff --git a/aidial_client/types/chat/request_param.py b/aidial_client/types/chat/request_param.py index 11bbf0f..9cb1965 100644 --- a/aidial_client/types/chat/request_param.py +++ b/aidial_client/types/chat/request_param.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Literal, Optional, Union +from typing import Literal from typing_extensions import Required, TypedDict @@ -20,31 +20,31 @@ class AttachmentParam(TypedDict, total=False): class CustomContentParam(TypedDict, total=False): - attachments: Optional[List[AttachmentParam]] - state: Optional[Dict] + attachments: list[AttachmentParam] | None + state: dict | None class SystemMessageParam(TypedDict, total=False): role: Required[Literal["system"]] content: Required[str] - custom_content: Optional[CustomContentParam] - name: Optional[str] + custom_content: CustomContentParam | None + name: str | None class UserMessageParam(TypedDict, total=False): role: Required[Literal["user"]] content: Required[str] - custom_content: Optional[CustomContentParam] - name: Optional[str] + custom_content: CustomContentParam | None + name: str | None class AssistantMessageParam(TypedDict, total=False): role: Required[Literal["assistant"]] - content: Optional[str] - custom_content: Optional[CustomContentParam] - function_call: Optional[FunctionCallParam] - tool_calls: List[ToolCallParam] - name: Optional[str] + content: str | None + custom_content: CustomContentParam | None + function_call: FunctionCallParam | None + tool_calls: list[ToolCallParam] + name: str | None class ToolMessageParam(TypedDict, total=False): @@ -60,10 +60,10 @@ class FunctionMessageParam(TypedDict, total=False): name: Required[str] -Message = Union[ - SystemMessageParam, - UserMessageParam, - AssistantMessageParam, - ToolMessageParam, - FunctionMessageParam, -] +Message = ( + SystemMessageParam + | UserMessageParam + | AssistantMessageParam + | ToolMessageParam + | FunctionMessageParam +) diff --git a/aidial_client/types/chat/response.py b/aidial_client/types/chat/response.py index a868ecb..5339443 100644 --- a/aidial_client/types/chat/response.py +++ b/aidial_client/types/chat/response.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Literal, Optional +from typing import Literal from aidial_client._compatibility.pydantic import PYDANTIC_V2 from aidial_client._compatibility.pydantic_v1 import root_validator @@ -9,12 +9,12 @@ class Attachment(ExtraAllowModel): - type: Optional[str] = None - title: Optional[str] = None - data: Optional[str] = None - url: Optional[str] = None - reference_type: Optional[str] = None - reference_url: Optional[str] = None + type: str | None = None + title: str | None = None + data: str | None = None + url: str | None = None + reference_type: str | None = None + reference_url: str | None = None if PYDANTIC_V2: @@ -39,8 +39,8 @@ def validate_data_or_url_v1(cls, values): class CustomContent(ExtraAllowModel): - attachments: Optional[List[Attachment]] = None - state: Optional[Dict] = None + attachments: list[Attachment] | None = None + state: dict | None = None class CompletionUsage(ExtraAllowModel): @@ -55,8 +55,8 @@ class FunctionCall(ExtraAllowModel): class FunctionCallDelta(ExtraAllowModel): - arguments: Optional[str] = None - name: Optional[str] = None + arguments: str | None = None + name: str | None = None class ChatCompletionMessageToolCall(ExtraAllowModel): @@ -67,52 +67,52 @@ class ChatCompletionMessageToolCall(ExtraAllowModel): class ToolCallDelta(ExtraAllowModel): index: int - id: Optional[str] = None - function: Optional[FunctionCallDelta] = None - type: Optional[Literal["function"]] = None + id: str | None = None + function: FunctionCallDelta | None = None + type: Literal["function"] | None = None class ChatCompletionMessage(ExtraAllowModel): role: Literal["assistant"] - content: Optional[str] = None - custom_content: Optional[CustomContent] = None - function_call: Optional[FunctionCall] = None - tool_calls: Optional[List[ChatCompletionMessageToolCall]] = None + content: str | None = None + custom_content: CustomContent | None = None + function_call: FunctionCall | None = None + tool_calls: list[ChatCompletionMessageToolCall] | None = None class ChatCompletionMessageDelta(ExtraAllowModel): - role: Optional[Literal["assistant"]] = None - content: Optional[str] = None - custom_content: Optional[CustomContent] = None - function_call: Optional[FunctionCallDelta] = None - tool_calls: Optional[List[ToolCallDelta]] = None + role: Literal["assistant"] | None = None + content: str | None = None + custom_content: CustomContent | None = None + function_call: FunctionCallDelta | None = None + tool_calls: list[ToolCallDelta] | None = None class Choice(ExtraAllowModel): index: int message: ChatCompletionMessage - finish_reason: Optional[str] + finish_reason: str | None class ChoiceDelta(ExtraAllowModel): index: int delta: ChatCompletionMessageDelta - finish_reason: Optional[str] = None + finish_reason: str | None = None class ChatCompletionResponse(ExtraAllowModel): id: str object: Literal["chat.completion"] - choices: List[Choice] + choices: list[Choice] created: int - model: Optional[str] = None - usage: Optional[CompletionUsage] = None + model: str | None = None + usage: CompletionUsage | None = None class ChatCompletionChunk(ExtraAllowModel): id: str object: Literal["chat.completion.chunk"] - choices: List[ChoiceDelta] + choices: list[ChoiceDelta] created: int - model: Optional[str] = None - usage: Optional[CompletionUsage] = None + model: str | None = None + usage: CompletionUsage | None = None diff --git a/aidial_client/types/deployment.py b/aidial_client/types/deployment.py index 91f2ee7..7925a12 100644 --- a/aidial_client/types/deployment.py +++ b/aidial_client/types/deployment.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Literal, Optional +from typing import Literal from aidial_client._internal_types._model import ExtraAllowModel @@ -8,42 +8,42 @@ class ScaleSettings(ExtraAllowModel): class Features(ExtraAllowModel): - rate: Optional[bool] = None - tokenize: Optional[bool] = None - temperature: Optional[bool] = None - truncate_prompt: Optional[bool] = None - configuration: Optional[bool] = None - system_prompt: Optional[bool] = None - tools: Optional[bool] = None - seed: Optional[bool] = None - url_attachments: Optional[bool] = None - folder_attachments: Optional[bool] = None - allow_resume: Optional[bool] = None - parallel_tool_calls: Optional[bool] = None - accessible_by_per_request_key: Optional[bool] = None - content_parts: Optional[bool] = None - cache: Optional[bool] = None - auto_caching: Optional[bool] = None - assistant_attachments_in_request: Optional[bool] = None - mcp: Optional[bool] = None + rate: bool | None = None + tokenize: bool | None = None + temperature: bool | None = None + truncate_prompt: bool | None = None + configuration: bool | None = None + system_prompt: bool | None = None + tools: bool | None = None + seed: bool | None = None + url_attachments: bool | None = None + folder_attachments: bool | None = None + allow_resume: bool | None = None + parallel_tool_calls: bool | None = None + accessible_by_per_request_key: bool | None = None + content_parts: bool | None = None + cache: bool | None = None + auto_caching: bool | None = None + assistant_attachments_in_request: bool | None = None + mcp: bool | None = None class DeploymentBase(ExtraAllowModel): id: str object: str - owner: Optional[str] = None - reference: Optional[str] = None - icon_url: Optional[str] = None - description: Optional[str] = None - display_name: Optional[str] = None - display_version: Optional[str] = None - status: Optional[str] = None + owner: str | None = None + reference: str | None = None + icon_url: str | None = None + description: str | None = None + display_name: str | None = None + display_version: str | None = None + status: str | None = None created_at: int - updated_at: Optional[int] = None - scale_settings: Optional[ScaleSettings] = None - defaults: Dict = {} - input_attachment_types: Optional[List[str]] = None - features: Optional[Features] = None + updated_at: int | None = None + scale_settings: ScaleSettings | None = None + defaults: dict = {} + input_attachment_types: list[str] | None = None + features: Features | None = None class Deployment(DeploymentBase): @@ -52,5 +52,5 @@ class Deployment(DeploymentBase): class DeploymentsResponse(ExtraAllowModel): - data: List[Deployment] + data: list[Deployment] object: Literal["list"] diff --git a/aidial_client/types/file.py b/aidial_client/types/file.py index ad7941c..6deaa8a 100644 --- a/aidial_client/types/file.py +++ b/aidial_client/types/file.py @@ -1,25 +1,22 @@ from pathlib import Path -from typing import Union import aiofiles import httpx class FileDownloadResponse: - def __init__(self, response: httpx.Response, filename: str): self._response = response self._filename = filename - def write_to(self, file: Union[str, Path]) -> None: + def write_to(self, file: str | Path) -> None: """ Write the content to a file """ with open(file, "wb") as f: - for chunk in self._response.iter_bytes(): - f.write(chunk) + f.writelines(self._response.iter_bytes()) - async def awrite_to(self, file: Union[str, Path]) -> None: + async def awrite_to(self, file: str | Path) -> None: """ Async write content to a file """ diff --git a/aidial_client/types/metadata.py b/aidial_client/types/metadata.py index 874c529..e5a62be 100644 --- a/aidial_client/types/metadata.py +++ b/aidial_client/types/metadata.py @@ -1,4 +1,4 @@ -from typing import List, Literal, Optional +from typing import Literal from aidial_client._compatibility.pydantic import PYDANTIC_V2 from aidial_client._internal_types._model import ExtraAllowModel @@ -17,8 +17,8 @@ class Config: alias_generator = to_camel allow_population_by_field_name = True - name: Optional[str] = None - parent_path: Optional[str] = None + name: str | None = None + parent_path: str | None = None bucket: str url: str node_type: Literal["FOLDER", "ITEM"] @@ -28,17 +28,17 @@ class Config: class FileItem(BaseMetadata): node_type: Literal["FOLDER", "ITEM"] resource_type: Literal["FILE"] - content_length: Optional[int] = None - content_type: Optional[str] = None + content_length: int | None = None + content_type: str | None = None class FileMetadata(BaseMetadata): node_type: Literal["FOLDER", "ITEM"] resource_type: Literal["FILE"] - content_length: Optional[int] = None - content_type: Optional[str] = None - items: Optional[List[FileItem]] = None - etag: Optional[str] = None + content_length: int | None = None + content_type: str | None = None + items: list[FileItem] | None = None + etag: str | None = None class ConversationItem(BaseMetadata): @@ -47,9 +47,9 @@ class ConversationItem(BaseMetadata): class ConversationMetadata(BaseMetadata): - content_length: Optional[int] = None - next_token: Optional[str] = None - items: Optional[List[ConversationItem]] + content_length: int | None = None + next_token: str | None = None + items: list[ConversationItem] | None resource_type: Literal["CONVERSATION"] @@ -59,7 +59,7 @@ class PromptItem(BaseMetadata): class PromptMetadata(BaseMetadata): - content_length: Optional[int] = None - next_token: Optional[str] = None - items: Optional[List[PromptItem]] + content_length: int | None = None + next_token: str | None = None + items: list[PromptItem] | None resource_type: Literal["PROMPT"] diff --git a/aidial_client/types/model.py b/aidial_client/types/model.py index 1c3b2cc..5364b65 100644 --- a/aidial_client/types/model.py +++ b/aidial_client/types/model.py @@ -1,15 +1,13 @@ -from typing import List, Optional - from aidial_client._internal_types._model import ExtraAllowModel class ModelCapabilities(ExtraAllowModel): - scale_types: List[str] = [] - completion: Optional[bool] = None - chat_completion: Optional[bool] = None - embeddings: Optional[bool] = None - fine_tune: Optional[bool] = None - inference: Optional[bool] = None + scale_types: list[str] = [] + completion: bool | None = None + chat_completion: bool | None = None + embeddings: bool | None = None + fine_tune: bool | None = None + inference: bool | None = None class ModelLimits(ExtraAllowModel): @@ -20,29 +18,29 @@ class ModelLimits(ExtraAllowModel): All fields are Optional here to accommodate both variants. """ - max_total_tokens: Optional[int] = None - max_prompt_tokens: Optional[int] = None - max_completion_tokens: Optional[int] = None + max_total_tokens: int | None = None + max_prompt_tokens: int | None = None + max_completion_tokens: int | None = None class ModelPricing(ExtraAllowModel): unit: str prompt: str - completion: Optional[str] = None + completion: str | None = None class ModelInfo(ExtraAllowModel): id: str model: str - display_name: Optional[str] = None - description: Optional[str] = None - owner: Optional[str] = None - object: Optional[str] = None - status: Optional[str] = None - created_at: Optional[int] = None - updated_at: Optional[int] = None - lifecycle_status: Optional[str] = None - tokenizer_model: Optional[str] = None - capabilities: Optional[ModelCapabilities] = None - limits: Optional[ModelLimits] = None - pricing: Optional[ModelPricing] = None + display_name: str | None = None + description: str | None = None + owner: str | None = None + object: str | None = None + status: str | None = None + created_at: int | None = None + updated_at: int | None = None + lifecycle_status: str | None = None + tokenizer_model: str | None = None + capabilities: ModelCapabilities | None = None + limits: ModelLimits | None = None + pricing: ModelPricing | None = None diff --git a/aidial_client/types/prompt.py b/aidial_client/types/prompt.py index 5b642bc..8fa5f1b 100644 --- a/aidial_client/types/prompt.py +++ b/aidial_client/types/prompt.py @@ -1,5 +1,3 @@ -from typing import Optional - from aidial_client._compatibility.pydantic import PYDANTIC_V2 from aidial_client._internal_types._model import ExtraAllowModel from aidial_client._utils._alias import to_camel @@ -22,4 +20,4 @@ class Config: id: str name: str folder_id: str - content: Optional[str] = None + content: str | None = None diff --git a/aidial_client/types/toolset.py b/aidial_client/types/toolset.py index 8d0ac94..077fb78 100644 --- a/aidial_client/types/toolset.py +++ b/aidial_client/types/toolset.py @@ -1,5 +1,3 @@ -from typing import List, Optional - from aidial_client._internal_types._model import ExtraAllowModel from aidial_client.types.deployment import Features @@ -7,18 +5,18 @@ class ToolsetInfo(ExtraAllowModel): id: str toolset: str - display_name: Optional[str] = None - display_version: Optional[str] = None - description: Optional[str] = None - icon_url: Optional[str] = None - owner: Optional[str] = None - object: Optional[str] = None - status: Optional[str] = None - created_at: Optional[int] = None - updated_at: Optional[int] = None - reference: Optional[str] = None - description_keywords: List[str] = [] - max_retry_attempts: Optional[int] = None - transport: Optional[str] = None - allowed_tools: List[str] = [] - features: Optional[Features] = None + display_name: str | None = None + display_version: str | None = None + description: str | None = None + icon_url: str | None = None + owner: str | None = None + object: str | None = None + status: str | None = None + created_at: int | None = None + updated_at: int | None = None + reference: str | None = None + description_keywords: list[str] = [] + max_retry_attempts: int | None = None + transport: str | None = None + allowed_tools: list[str] = [] + features: Features | None = None diff --git a/noxfile.py b/noxfile.py index 002a113..c7eec81 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,30 +1,19 @@ -from urllib.parse import urljoin - import nox nox.options.reuse_existing_virtualenvs = True -SRC = "." -README = urljoin(SRC, "README.md") - - -def format_with_args(session: nox.Session, *args): - session.run("autoflake", *args) - session.run("isort", *args) - session.run("black", *args) +SRC = ["aidial_client", "tests", "noxfile.py"] @nox.session def lint(session: nox.Session): """Runs linters and fixers""" try: - session.run("poetry", "install", external=True) - session.run("poetry", "check", "--lock", external=True) - session.run("pyright", SRC) - session.run("flake8", SRC) - session.run("codespell", SRC) - session.run("blacken-docs", README) - format_with_args(session, SRC, "--check") + session.run("poetry", "install", "--all-extras", external=True) + session.run("poetry", "check", "--lock", "--strict", external=True) + session.run("ruff", "check", *SRC) + session.run("ruff", "format", "--check", *SRC) + session.run("pyright", *SRC) except Exception: session.error( "linting has failed. Run 'make format' " @@ -38,7 +27,7 @@ def coverage(session: nox.Session) -> None: session.run("poetry", "install", external=True) session.run( "pytest", - f"--cov={SRC}", + f"--cov={SRC[0]}", "--cov-report=xml", "--cov-report=term", "--ignore=tests/integration", @@ -49,8 +38,9 @@ def coverage(session: nox.Session) -> None: @nox.session def format(session: nox.Session): """Runs linters and fixers""" - session.run("poetry", "install", external=True) - format_with_args(session, SRC) + session.run("poetry", "install", "--only", "lint", external=True) + session.run("ruff", "check", "--fix", *SRC) + session.run("ruff", "format", *SRC) @nox.session(python=["3.10", "3.11", "3.12", "3.13"]) diff --git a/poetry.lock b/poetry.lock index ecc4eeb..98c7e52 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. [[package]] name = "aiofiles" @@ -62,90 +62,6 @@ files = [ [package.extras] test = ["coverage", "mypy", "pexpect", "ruff", "wheel"] -[[package]] -name = "autoflake" -version = "2.3.1" -description = "Removes unused imports and unused variables" -optional = false -python-versions = ">=3.8" -groups = ["lint"] -files = [ - {file = "autoflake-2.3.1-py3-none-any.whl", hash = "sha256:3ae7495db9084b7b32818b4140e6dc4fc280b712fb414f5b8fe57b0a8e85a840"}, - {file = "autoflake-2.3.1.tar.gz", hash = "sha256:c98b75dc5b0a86459c4f01a1d32ac7eb4338ec4317a4469515ff1e687ecd909e"}, -] - -[package.dependencies] -pyflakes = ">=3.0.0" -tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} - -[[package]] -name = "black" -version = "26.3.1" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.10" -groups = ["lint"] -files = [ - {file = "black-26.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:86a8b5035fce64f5dcd1b794cf8ec4d31fe458cf6ce3986a30deb434df82a1d2"}, - {file = "black-26.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5602bdb96d52d2d0672f24f6ffe5218795736dd34807fd0fd55ccd6bf206168b"}, - {file = "black-26.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c54a4a82e291a1fee5137371ab488866b7c86a3305af4026bdd4dc78642e1ac"}, - {file = "black-26.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:6e131579c243c98f35bce64a7e08e87fb2d610544754675d4a0e73a070a5aa3a"}, - {file = "black-26.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:5ed0ca58586c8d9a487352a96b15272b7fa55d139fc8496b519e78023a8dab0a"}, - {file = "black-26.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff"}, - {file = "black-26.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bf162ed91a26f1adba8efda0b573bc6924ec1408a52cc6f82cb73ec2b142c"}, - {file = "black-26.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:474c27574d6d7037c1bc875a81d9be0a9a4f9ee95e62800dab3cfaadbf75acd5"}, - {file = "black-26.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e9d0d86df21f2e1677cc4bd090cd0e446278bcbbe49bf3659c308c3e402843e"}, - {file = "black-26.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9a5e9f45e5d5e1c5b5c29b3bd4265dcc90e8b92cf4534520896ed77f791f4da5"}, - {file = "black-26.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1"}, - {file = "black-26.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f"}, - {file = "black-26.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7"}, - {file = "black-26.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983"}, - {file = "black-26.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb"}, - {file = "black-26.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54"}, - {file = "black-26.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f"}, - {file = "black-26.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56"}, - {file = "black-26.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839"}, - {file = "black-26.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2"}, - {file = "black-26.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78"}, - {file = "black-26.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568"}, - {file = "black-26.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f"}, - {file = "black-26.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c"}, - {file = "black-26.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1"}, - {file = "black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b"}, - {file = "black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=1.0.0" -platformdirs = ">=2" -pytokens = ">=0.4.0,<0.5.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.10)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2) ; sys_platform != \"win32\"", "winloop (>=0.5.0) ; sys_platform == \"win32\""] - -[[package]] -name = "blacken-docs" -version = "1.18.0" -description = "Run Black on Python code blocks in documentation files." -optional = false -python-versions = ">=3.8" -groups = ["lint"] -files = [ - {file = "blacken_docs-1.18.0-py3-none-any.whl", hash = "sha256:64f592246784131e9f84dad1db397f44eeddc77fdf01726bab920a3f00a3815c"}, - {file = "blacken_docs-1.18.0.tar.gz", hash = "sha256:47bed628679d008a8eb55d112df950582e68d0f57615223929e366348d935444"}, -] - -[package.dependencies] -black = ">=22.1" - [[package]] name = "certifi" version = "2024.7.4" @@ -170,51 +86,18 @@ files = [ {file = "cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132"}, ] -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -groups = ["lint"] -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "codespell" -version = "2.3.0" -description = "Codespell" -optional = false -python-versions = ">=3.8" -groups = ["lint"] -files = [ - {file = "codespell-2.3.0-py3-none-any.whl", hash = "sha256:a9c7cef2501c9cfede2110fd6d4e5e62296920efe9abfb84648df866e47f58d1"}, - {file = "codespell-2.3.0.tar.gz", hash = "sha256:360c7d10f75e65f67bad720af7007e1060a5d395670ec11a7ed1fed9dd17471f"}, -] - -[package.extras] -dev = ["Pygments", "build", "chardet", "pre-commit", "pytest", "pytest-cov", "pytest-dependency", "ruff", "tomli", "twine"] -hard-encoding-detection = ["chardet"] -toml = ["tomli ; python_version < \"3.11\""] -types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency"] - [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "lint", "test"] +groups = ["main", "test"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {main = "platform_system == \"Windows\"", lint = "platform_system == \"Windows\"", test = "sys_platform == \"win32\""} +markers = {main = "platform_system == \"Windows\"", test = "sys_platform == \"win32\""} [[package]] name = "colorlog" @@ -374,23 +257,6 @@ files = [ {file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"}, ] -[[package]] -name = "flake8" -version = "6.1.0" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.8.1" -groups = ["lint"] -files = [ - {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, - {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.11.0,<2.12.0" -pyflakes = ">=3.1.0,<3.2.0" - [[package]] name = "h11" version = "0.16.0" @@ -492,21 +358,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -groups = ["lint"] -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - [[package]] name = "jiter" version = "0.5.0" @@ -578,30 +429,6 @@ files = [ {file = "jiter-0.5.0.tar.gz", hash = "sha256:1d916ba875bcab5c5f7d927df998c4cb694d27dceddf3392e58beaf10563368a"}, ] -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -groups = ["lint"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -groups = ["lint"] -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - [[package]] name = "nodeenv" version = "1.9.1" @@ -668,37 +495,19 @@ version = "24.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["lint", "test"] +groups = ["test"] files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] -[[package]] -name = "pathspec" -version = "1.0.4" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.9" -groups = ["lint"] -files = [ - {file = "pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723"}, - {file = "pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645"}, -] - -[package.extras] -hyperscan = ["hyperscan (>=0.7)"] -optional = ["typing-extensions (>=4)"] -re2 = ["google-re2 (>=1.1)"] -tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] - [[package]] name = "platformdirs" version = "4.2.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" -groups = ["dev", "lint", "test"] +groups = ["dev", "test"] files = [ {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, @@ -744,18 +553,6 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" -[[package]] -name = "pycodestyle" -version = "2.11.1" -description = "Python style guide checker" -optional = false -python-versions = ">=3.8" -groups = ["lint"] -files = [ - {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, - {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, -] - [[package]] name = "pydantic" version = "2.8.2" @@ -881,18 +678,6 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" -[[package]] -name = "pyflakes" -version = "3.1.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.8" -groups = ["lint"] -files = [ - {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, - {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, -] - [[package]] name = "pygments" version = "2.20.0" @@ -910,22 +695,24 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyright" -version = "1.1.372" +version = "1.1.385" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" groups = ["lint"] files = [ - {file = "pyright-1.1.372-py3-none-any.whl", hash = "sha256:25b15fb8967740f0949fd35b963777187f0a0404c0bd753cc966ec139f3eaa0b"}, - {file = "pyright-1.1.372.tar.gz", hash = "sha256:a9f5e0daa955daaa17e3d1ef76d3623e75f8afd5e37b437d3ff84d5b38c15420"}, + {file = "pyright-1.1.385-py3-none-any.whl", hash = "sha256:e5b9a1b8d492e13004d822af94d07d235f2c7c158457293b51ab2214c8c5b375"}, + {file = "pyright-1.1.385.tar.gz", hash = "sha256:1bf042b8f080441534aa02101dea30f8fc2efa8f7b6f1ab05197c21317f5bfa7"}, ] [package.dependencies] nodeenv = ">=1.6.0" +typing-extensions = ">=4.1" [package.extras] -all = ["twine (>=3.4.1)"] +all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"] dev = ["twine (>=3.4.1)"] +nodejs = ["nodejs-wheel-binaries"] [[package]] name = "pytest" @@ -989,61 +776,6 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] -[[package]] -name = "pytokens" -version = "0.4.1" -description = "A Fast, spec compliant Python 3.14+ tokenizer that runs on older Pythons." -optional = false -python-versions = ">=3.8" -groups = ["lint"] -files = [ - {file = "pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5"}, - {file = "pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe"}, - {file = "pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c"}, - {file = "pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7"}, - {file = "pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2"}, - {file = "pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440"}, - {file = "pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc"}, - {file = "pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d"}, - {file = "pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16"}, - {file = "pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6"}, - {file = "pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083"}, - {file = "pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1"}, - {file = "pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1"}, - {file = "pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9"}, - {file = "pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68"}, - {file = "pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b"}, - {file = "pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f"}, - {file = "pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1"}, - {file = "pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4"}, - {file = "pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78"}, - {file = "pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321"}, - {file = "pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa"}, - {file = "pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d"}, - {file = "pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324"}, - {file = "pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9"}, - {file = "pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb"}, - {file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3"}, - {file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975"}, - {file = "pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a"}, - {file = "pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918"}, - {file = "pytokens-0.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:da5baeaf7116dced9c6bb76dc31ba04a2dc3695f3d9f74741d7910122b456edc"}, - {file = "pytokens-0.4.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11edda0942da80ff58c4408407616a310adecae1ddd22eef8c692fe266fa5009"}, - {file = "pytokens-0.4.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0fc71786e629cef478cbf29d7ea1923299181d0699dbe7c3c0f4a583811d9fc1"}, - {file = "pytokens-0.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dcafc12c30dbaf1e2af0490978352e0c4041a7cde31f4f81435c2a5e8b9cabb6"}, - {file = "pytokens-0.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:42f144f3aafa5d92bad964d471a581651e28b24434d184871bd02e3a0d956037"}, - {file = "pytokens-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:34bcc734bd2f2d5fe3b34e7b3c0116bfb2397f2d9666139988e7a3eb5f7400e3"}, - {file = "pytokens-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941d4343bf27b605e9213b26bfa1c4bf197c9c599a9627eb7305b0defcfe40c1"}, - {file = "pytokens-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ad72b851e781478366288743198101e5eb34a414f1d5627cdd585ca3b25f1db"}, - {file = "pytokens-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:682fa37ff4d8e95f7df6fe6fe6a431e8ed8e788023c6bcc0f0880a12eab80ad1"}, - {file = "pytokens-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:30f51edd9bb7f85c748979384165601d028b84f7bd13fe14d3e065304093916a"}, - {file = "pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de"}, - {file = "pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a"}, -] - -[package.extras] -dev = ["black", "build", "mypy", "pytest", "pytest-cov", "setuptools", "tox", "twine", "wheel"] - [[package]] name = "pyyaml" version = "6.0.3" @@ -1127,6 +859,35 @@ files = [ {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, ] +[[package]] +name = "ruff" +version = "0.12.11" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["lint"] +files = [ + {file = "ruff-0.12.11-py3-none-linux_armv6l.whl", hash = "sha256:93fce71e1cac3a8bf9200e63a38ac5c078f3b6baebffb74ba5274fb2ab276065"}, + {file = "ruff-0.12.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8e33ac7b28c772440afa80cebb972ffd823621ded90404f29e5ab6d1e2d4b93"}, + {file = "ruff-0.12.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d69fb9d4937aa19adb2e9f058bc4fbfe986c2040acb1a4a9747734834eaa0bfd"}, + {file = "ruff-0.12.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:411954eca8464595077a93e580e2918d0a01a19317af0a72132283e28ae21bee"}, + {file = "ruff-0.12.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a2c0a2e1a450f387bf2c6237c727dd22191ae8c00e448e0672d624b2bbd7fb0"}, + {file = "ruff-0.12.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ca4c3a7f937725fd2413c0e884b5248a19369ab9bdd850b5781348ba283f644"}, + {file = "ruff-0.12.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4d1df0098124006f6a66ecf3581a7f7e754c4df7644b2e6704cd7ca80ff95211"}, + {file = "ruff-0.12.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a8dd5f230efc99a24ace3b77e3555d3fbc0343aeed3fc84c8d89e75ab2ff793"}, + {file = "ruff-0.12.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dc75533039d0ed04cd33fb8ca9ac9620b99672fe7ff1533b6402206901c34ee"}, + {file = "ruff-0.12.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fc58f9266d62c6eccc75261a665f26b4ef64840887fc6cbc552ce5b29f96cc8"}, + {file = "ruff-0.12.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5a0113bd6eafd545146440225fe60b4e9489f59eb5f5f107acd715ba5f0b3d2f"}, + {file = "ruff-0.12.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0d737b4059d66295c3ea5720e6efc152623bb83fde5444209b69cd33a53e2000"}, + {file = "ruff-0.12.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:916fc5defee32dbc1fc1650b576a8fed68f5e8256e2180d4d9855aea43d6aab2"}, + {file = "ruff-0.12.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c984f07d7adb42d3ded5be894fb4007f30f82c87559438b4879fe7aa08c62b39"}, + {file = "ruff-0.12.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e07fbb89f2e9249f219d88331c833860489b49cdf4b032b8e4432e9b13e8a4b9"}, + {file = "ruff-0.12.11-py3-none-win32.whl", hash = "sha256:c792e8f597c9c756e9bcd4d87cf407a00b60af77078c96f7b6366ea2ce9ba9d3"}, + {file = "ruff-0.12.11-py3-none-win_amd64.whl", hash = "sha256:a3283325960307915b6deb3576b96919ee89432ebd9c48771ca12ee8afe4a0fd"}, + {file = "ruff-0.12.11-py3-none-win_arm64.whl", hash = "sha256:bae4d6e6a2676f8fb0f98b74594a048bae1b944aab17e9f5d504062303c6dbea"}, + {file = "ruff-0.12.11.tar.gz", hash = "sha256:c6b09ae8426a65bbee5425b9d0b82796dbb07cb1af045743c79bfb163001165d"}, +] + [[package]] name = "sniffio" version = "1.3.1" @@ -1145,12 +906,12 @@ version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" -groups = ["lint", "test"] +groups = ["test"] +markers = "python_full_version <= \"3.11.0a6\"" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -markers = {lint = "python_version == \"3.10\"", test = "python_full_version <= \"3.11.0a6\""} [[package]] name = "tqdm" @@ -1184,7 +945,7 @@ files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] -markers = {dev = "python_version == \"3.10\"", lint = "python_version == \"3.10\"", test = "python_version == \"3.10\""} +markers = {dev = "python_version == \"3.10\"", test = "python_version == \"3.10\""} [[package]] name = "virtualenv" @@ -1211,4 +972,4 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.14" -content-hash = "b012381e39736376734d10f42fecd57d3160ef8d288a53dbdd41f10c94b21ba0" +content-hash = "02233595b344b3aeed71ffa965b3b539e7fca59d97af94c8344e56fa02deac0b" diff --git a/pyproject.toml b/pyproject.toml index da90a45..c60194e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,9 +5,7 @@ description = "A Python client library for the AI DIAL API" readme = "README.md" license = "Apache-2.0" requires-python = ">=3.10,<3.14" -authors = [ - { name = "DIALX", email = "SpecialEPM-DIALDevTeam@epam.com" }, -] +authors = [{ name = "DIALX", email = "SpecialEPM-DIALDevTeam@epam.com" }] dependencies = [ "openai>=1.1.0,<3", "httpx>=0.25.0,<1", @@ -29,18 +27,13 @@ build-backend = "poetry.core.masonry.api" [tool.poetry.group.test.dependencies] pytest = "^9.0.3" pytest-asyncio = "^0.21.0" -nox = "^2024.4.15" pytest-cov = "^5.0" +nox = "^2024.4.15" coverage = "^7.6" [tool.poetry.group.lint.dependencies] -flake8 = "^6.0.0" -black = ">=23.3,<27.0" -isort = "^5.12.0" -pyright = "^1.1.324" -autoflake = "^2.2.0" -codespell = "^2.3.0" -blacken-docs = "^1.18.0" +pyright = "1.1.385" +ruff = "0.12.11" [tool.poetry.group.dev.dependencies] pre-commit = "^4.5.1" @@ -52,46 +45,49 @@ testpaths = ["tests"] typeCheckingMode = "basic" reportUnusedVariable = "error" reportIncompatibleMethodOverride = "error" -exclude = [ - ".git", - "**/.venv", - ".nox", - ".pytest_cache", - "**/__pycache__", - "build", -] +exclude = ["**/node_modules", "**/__pycache__", "**/.*", ".venv"] -[tool.black] +[tool.ruff] line-length = 80 -target-version = ["py310"] -exclude = ''' -/( - \.git - | \.venv - | \.nox - | \.pytest_cache - | \.__pycache__ -)/ -''' +target-version = "py310" -[tool.isort] -line_length = 80 -profile = "black" +[tool.ruff.lint] +select = [ + "ERA", # flake8-eradicate + "B", # flake8-builtins + "C4", # flake8-comprehensions + "SIM", # flake8-simplify + "B9", # flake8-bugbear + "E", # pycodestyle + "W", # pycodestyle + "F", # pyflakes + "I", # flake8-isort + "S", # flake8-bandit + "FURB", # refurb + "UP", # pyupgrade +] -[tool.autoflake] -ignore_init_module_imports = true -remove_all_unused_imports = true -in_place = true -recursive = true -quiet = true -exclude = [".nox", ".pytest_cache", "\\.venv"] +ignore = [ + "S101", # Use of `assert` detected + "B904", # Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` +] + +[tool.ruff.lint.per-file-ignores] +"tests/*" = [ + "E501", # String literal is too long + "B011", # Do not `assert False` + "B006", # Do not use mutable data structures for argument defaults +] +"*/__init__.py" = [ + "F401", # imported but unused +] [tool.coverage.run] source = ["."] omit = [ - "tests/*", - "*/__init__.py", - "noxfile.py", - 'aidial_client/_compatibility/pydantic_v1.py', - 'htmlcov/*', + "tests/*", + "*/__init__.py", + "noxfile.py", + 'aidial_client/_compatibility/pydantic_v1.py', + 'htmlcov/*', ] diff --git a/tests/client_mock.py b/tests/client_mock.py index fb46ade..15971de 100644 --- a/tests/client_mock.py +++ b/tests/client_mock.py @@ -1,4 +1,5 @@ -from typing import Any, AsyncIterator, Dict, Iterator, List, Optional +from collections.abc import AsyncIterator, Iterator +from typing import Any import httpx from httpx._content import AsyncIteratorByteStream, IteratorByteStream @@ -8,11 +9,10 @@ class MockStreamIterator(IteratorByteStream, AsyncIteratorByteStream): - def __init__(self, mock_chunks: List[bytes]): + def __init__(self, mock_chunks: list[bytes]): class Stream: def __iter__(self) -> Iterator[bytes]: - for chunk in mock_chunks: - yield chunk + yield from mock_chunks async def __aiter__(self) -> AsyncIterator[bytes]: for chunk in mock_chunks: @@ -23,10 +23,10 @@ async def __aiter__(self) -> AsyncIterator[bytes]: def get_client_mock( - status_code: Optional[int], - json_mock: Optional[Dict[str, Any]] = None, - stream_chunks_mock: Optional[List[bytes]] = None, - exception_mock: Optional[Exception] = None, + status_code: int | None, + json_mock: dict[str, Any] | None = None, + stream_chunks_mock: list[bytes] | None = None, + exception_mock: Exception | None = None, ) -> Dial: client_mock = Dial( api_key="dummy", @@ -60,10 +60,10 @@ def send_mock(request: httpx.Request, **kwargs): def get_async_client_mock( - status_code: Optional[int], - json_mock: Optional[Dict[str, Any]] = None, - stream_chunks_mock: Optional[List[bytes]] = None, - exception_mock: Optional[Exception] = None, + status_code: int | None, + json_mock: dict[str, Any] | None = None, + stream_chunks_mock: list[bytes] | None = None, + exception_mock: Exception | None = None, ) -> AsyncDial: client_mock = AsyncDial( api_key="dummy", diff --git a/tests/integration/fixtures.py b/tests/integration/fixtures.py index 7927dd6..71796a1 100644 --- a/tests/integration/fixtures.py +++ b/tests/integration/fixtures.py @@ -1,3 +1,4 @@ +import contextlib import os import uuid @@ -35,19 +36,16 @@ def async_client(dial_url, dial_api_key): def test_deployment(sync_client: Dial) -> str: deployments = sync_client.deployments.list() assert len(deployments) - deployment = next((d for d in deployments if d.id.startswith("gpt-"))) + deployment = next(d for d in deployments if d.id.startswith("gpt-")) assert deployment return deployment.id @pytest.fixture def absent_test_file(sync_client): - def _save_delete_file(p): - try: + with contextlib.suppress(ResourceNotFoundError): sync_client.files.delete(p) - except ResourceNotFoundError: - pass unique_name = f"test-file-{uuid.uuid4()}.txt" full_path = sync_client.my_files_home() / unique_name diff --git a/tests/integration/test_async_completions.py b/tests/integration/test_async_completions.py index 6246877..e3645db 100644 --- a/tests/integration/test_async_completions.py +++ b/tests/integration/test_async_completions.py @@ -73,7 +73,7 @@ async def test_completions_without_streaming( async def test_completions_with_streaming(async_client: AsyncDial): deployments = await async_client.deployments.list() assert len(deployments) - deployment = next((d for d in deployments if d.id.startswith("gpt-"))) + deployment = next(d for d in deployments if d.id.startswith("gpt-")) assert deployment completion = await async_client.chat.completions.create( deployment_name=deployment.id, diff --git a/tests/integration/test_async_prompts.py b/tests/integration/test_async_prompts.py index 4b54780..81e3a5d 100644 --- a/tests/integration/test_async_prompts.py +++ b/tests/integration/test_async_prompts.py @@ -1,3 +1,4 @@ +import contextlib import uuid import pytest @@ -12,10 +13,8 @@ async def _delete_if_exists(async_client: AsyncDial, url: str) -> None: - try: + with contextlib.suppress(ResourceNotFoundError): await async_client.prompts.delete(url) - except ResourceNotFoundError: - pass def _create_prompt( diff --git a/tests/integration/test_sync_completions.py b/tests/integration/test_sync_completions.py index a35b61c..435b90c 100644 --- a/tests/integration/test_sync_completions.py +++ b/tests/integration/test_sync_completions.py @@ -65,7 +65,7 @@ def test_default_api_version( def test_completions_with_streaming(sync_client: Dial): deployments = sync_client.deployments.list() assert len(deployments) - deployment = next((d for d in deployments if d.id.startswith("gpt-"))) + deployment = next(d for d in deployments if d.id.startswith("gpt-")) assert deployment completion = sync_client.chat.completions.create( deployment_name=deployment.id, diff --git a/tests/integration/test_sync_files.py b/tests/integration/test_sync_files.py index 4a54490..050f7db 100644 --- a/tests/integration/test_sync_files.py +++ b/tests/integration/test_sync_files.py @@ -31,7 +31,7 @@ def test_upload(sync_client: Dial): download_result = sync_client.files.download( url=sync_client.my_files_home() / file_path ) - assert b"".join([chunk for chunk in download_result]) == file_content + assert b"".join(list(download_result)) == file_content assert download_result.get_content() == file_content diff --git a/tests/integration/test_sync_prompts.py b/tests/integration/test_sync_prompts.py index d591f23..78da5d0 100644 --- a/tests/integration/test_sync_prompts.py +++ b/tests/integration/test_sync_prompts.py @@ -1,3 +1,4 @@ +import contextlib import uuid import pytest @@ -12,10 +13,8 @@ def _delete_if_exists(sync_client: Dial, url: str) -> None: - try: + with contextlib.suppress(ResourceNotFoundError): sync_client.prompts.delete(url) - except ResourceNotFoundError: - pass def _create_prompt( diff --git a/tests/resources/completions/test_completions_streaming_tool_call.py b/tests/resources/completions/test_completions_streaming_tool_call.py index a5b6cfb..b9a3a44 100644 --- a/tests/resources/completions/test_completions_streaming_tool_call.py +++ b/tests/resources/completions/test_completions_streaming_tool_call.py @@ -1,5 +1,5 @@ import inspect -from typing import Iterable, List +from collections.abc import Iterable import pytest @@ -25,7 +25,7 @@ }, } -_DELTA_CHUNKS: List[dict] = [ +_DELTA_CHUNKS: list[dict] = [ { "role": "assistant", "tool_calls": [ @@ -128,7 +128,7 @@ {"role": None, "tool_calls": None}, ] -_STREAM_CHUNKS_MOCK: List[bytes] = [ +_STREAM_CHUNKS_MOCK: list[bytes] = [ *[ create_sse_data_field(create_mock_chunk(delta=delta)) for delta in _DELTA_CHUNKS @@ -146,13 +146,14 @@ ] -def _validate_chunks(chunks: List[ChatCompletionChunk]): +def _validate_chunks(chunks: list[ChatCompletionChunk]): assert all(len(chunk.choices) for chunk in chunks) assert all(chunk.choices[0].delta for chunk in chunks) assert all(chunk.choices[0].delta.tool_calls for chunk in chunks[:-2]) assert all(chunk.choices[0].delta.content is None for chunk in chunks) total_arguments = "".join( - chunk.choices[0].delta.tool_calls[0].function.arguments or "" for chunk in chunks[:-2] # type: ignore + chunk.choices[0].delta.tool_calls[0].function.arguments or "" # type: ignore[index,union-attr] + for chunk in chunks[:-2] ) assert total_arguments == '{"request":"current weather in Paris"}' @@ -178,7 +179,7 @@ def test_sync_streaming_tool_call(): stream=True, ) assert isinstance(response, Iterable) - chunks = [chunk for chunk in response] + chunks = list(response) assert all(isinstance(chunk, ChatCompletionChunk) for chunk in chunks) _validate_chunks(chunks) diff --git a/tests/resources/completions/test_completions_streaming_vanilla.py b/tests/resources/completions/test_completions_streaming_vanilla.py index dcb8492..dac087b 100644 --- a/tests/resources/completions/test_completions_streaming_vanilla.py +++ b/tests/resources/completions/test_completions_streaming_vanilla.py @@ -1,5 +1,5 @@ import inspect -from typing import Iterable, List +from collections.abc import Iterable import pytest @@ -7,7 +7,7 @@ from tests.client_mock import get_async_client_mock, get_client_mock from tests.utils.chunks import create_mock_chunk, create_sse_data_field -STREAM_CHUNKS_MOCK: List[bytes] = [ +STREAM_CHUNKS_MOCK: list[bytes] = [ create_sse_data_field( create_mock_chunk(delta={"content": "", "role": "assistant"}) ) @@ -25,7 +25,7 @@ ] -def _validate_chunks(chunks: List[ChatCompletionChunk]): +def _validate_chunks(chunks: list[ChatCompletionChunk]): assert all(len(chunk.choices) for chunk in chunks) assert all(chunk.choices[0].delta for chunk in chunks) # All except last chunk has some content @@ -58,7 +58,7 @@ def test_sync_streaming(): stream=True, ) assert isinstance(response, Iterable) - chunks = [chunk for chunk in response] + chunks = list(response) assert all(isinstance(chunk, ChatCompletionChunk) for chunk in chunks) _validate_chunks(chunks) diff --git a/tests/resources/files/test_move_copy.py b/tests/resources/files/test_move_copy.py index b50650b..f517db8 100644 --- a/tests/resources/files/test_move_copy.py +++ b/tests/resources/files/test_move_copy.py @@ -1,5 +1,5 @@ import json -from typing import Any, Dict, List +from typing import Any from unittest.mock import AsyncMock, Mock import httpx @@ -10,7 +10,7 @@ from aidial_client._exception import InvalidDialURLError -def _make_capturing_client(captured: List[httpx.Request]) -> Dial: +def _make_capturing_client(captured: list[httpx.Request]) -> Dial: client = Dial(api_key="dummy", base_url="http://dial.core") def send_mock(request: httpx.Request, **_: Any) -> httpx.Response: @@ -25,7 +25,7 @@ def send_mock(request: httpx.Request, **_: Any) -> httpx.Response: def _make_async_capturing_client( - captured: List[httpx.Request], + captured: list[httpx.Request], ) -> AsyncDial: client = AsyncDial(api_key="dummy", base_url="http://dial.core") @@ -40,7 +40,7 @@ async def send_mock(request: httpx.Request, **_: Any) -> httpx.Response: return client -def _body(request: httpx.Request) -> Dict[str, Any]: +def _body(request: httpx.Request) -> dict[str, Any]: return json.loads(request.content.decode()) @@ -54,7 +54,7 @@ def _body(request: httpx.Request) -> Dict[str, Any]: def test_move_copy_returns_none_and_sends_expected_body( method: str, endpoint: str ): - captured: List[httpx.Request] = [] + captured: list[httpx.Request] = [] client = _make_capturing_client(captured) result = getattr(client.files, method)( @@ -76,7 +76,7 @@ def test_move_copy_returns_none_and_sends_expected_body( @parametrize_method def test_overwrite_round_trips(method: str): - captured: List[httpx.Request] = [] + captured: list[httpx.Request] = [] client = _make_capturing_client(captured) getattr(client.files, method)( @@ -90,7 +90,7 @@ def test_overwrite_round_trips(method: str): @parametrize_method def test_accepts_pureposixpath_and_absolute_urls(method: str): - captured: List[httpx.Request] = [] + captured: list[httpx.Request] = [] client = _make_capturing_client(captured) getattr(client.files, method)( @@ -109,7 +109,7 @@ def test_accepts_pureposixpath_and_absolute_urls(method: str): ["source", "destination"], ) def test_rejects_non_files_urls(method: str, bad_arg: str): - captured: List[httpx.Request] = [] + captured: list[httpx.Request] = [] client = _make_capturing_client(captured) kwargs = { @@ -129,7 +129,7 @@ def test_rejects_non_files_urls(method: str, bad_arg: str): async def test_move_copy_async_returns_none_and_sends_expected_body( method: str, endpoint: str ): - captured: List[httpx.Request] = [] + captured: list[httpx.Request] = [] client = _make_async_capturing_client(captured) result = await getattr(client.files, method)( @@ -153,7 +153,7 @@ async def test_move_copy_async_returns_none_and_sends_expected_body( @parametrize_method @pytest.mark.asyncio async def test_async_rejects_non_files_urls(method: str): - captured: List[httpx.Request] = [] + captured: list[httpx.Request] = [] client = _make_async_capturing_client(captured) with pytest.raises(InvalidDialURLError, match="Invalid resource type"): diff --git a/tests/resources/test_client_channel.py b/tests/resources/test_client_channel.py index d85d911..207d92d 100644 --- a/tests/resources/test_client_channel.py +++ b/tests/resources/test_client_channel.py @@ -1,7 +1,7 @@ import json import logging from http import HTTPStatus -from typing import Any, List +from typing import Any import httpx import pytest @@ -21,7 +21,7 @@ ) -def _sse_chunks(*lines: str) -> List[bytes]: +def _sse_chunks(*lines: str) -> list[bytes]: """Encode a sequence of SSE lines as one byte stream chunk.""" return [("\n".join(lines) + "\n").encode()] @@ -30,7 +30,7 @@ def _data(payload: Any) -> str: return f"data: {json.dumps(payload)}" -def _single_event(payload: Any) -> List[bytes]: +def _single_event(payload: Any) -> list[bytes]: return _sse_chunks(_data(payload), "") @@ -428,12 +428,14 @@ def test_interact_truncated_stream_warns_and_no_phantom_event_sync(caplog): # warning must be emitted by the SSE parser. chunks = _sse_chunks('data: {"jsonrpc":"2.0","resu') client = get_client_mock(status_code=200, stream_chunks_mock=chunks) - with caplog.at_level(logging.WARNING, logger="aidial_client"): - with pytest.raises(DialException) as exc_info: - client.client_channel._interact( - channel_id="ch", - requests=[JsonRpcRequest(jsonrpc="2.0", method="m", id="1")], - ) + with ( + caplog.at_level(logging.WARNING, logger="aidial_client"), + pytest.raises(DialException) as exc_info, + ): + client.client_channel._interact( + channel_id="ch", + requests=[JsonRpcRequest(jsonrpc="2.0", method="m", id="1")], + ) assert exc_info.value.status_code == HTTPStatus.GATEWAY_TIMEOUT assert any( "Uncommitted data chunks in SSE stream" in rec.message diff --git a/tests/resources/test_deployments.py b/tests/resources/test_deployments.py index 92ddef1..961bf27 100644 --- a/tests/resources/test_deployments.py +++ b/tests/resources/test_deployments.py @@ -28,11 +28,6 @@ } -# --------------------------------------------------------------------------- -# deployments.get() -# --------------------------------------------------------------------------- - - def test_get_deployment(): client = get_client_mock(status_code=200, json_mock=DEPLOYMENT_MOCK) result = client.deployments.get("gpt-4") @@ -71,11 +66,6 @@ async def test_async_get_deployment_http_error(): await client.deployments.get("gpt-4") -# --------------------------------------------------------------------------- -# deployments.get_config() -# --------------------------------------------------------------------------- - - def test_get_deployment_config(): client = get_client_mock(status_code=200, json_mock=CONFIG_MOCK) result = client.deployments.get_configuration_schema("gpt-4") diff --git a/tests/resources/test_prompts.py b/tests/resources/test_prompts.py index 9bf6453..db994ae 100644 --- a/tests/resources/test_prompts.py +++ b/tests/resources/test_prompts.py @@ -1,5 +1,5 @@ import json -from typing import Any, Dict, List +from typing import Any from unittest.mock import AsyncMock, Mock import httpx @@ -41,7 +41,7 @@ } -def _make_capturing_client(captured: List[httpx.Request]) -> Dial: +def _make_capturing_client(captured: list[httpx.Request]) -> Dial: client = Dial(api_key="dummy", base_url="http://dial.core") def send_mock(request: httpx.Request, **_: Any) -> httpx.Response: @@ -57,12 +57,12 @@ def send_mock(request: httpx.Request, **_: Any) -> httpx.Response: return client -def _body(request: httpx.Request) -> Dict[str, Any]: +def _body(request: httpx.Request) -> dict[str, Any]: return json.loads(request.content.decode()) def _make_async_capturing_client( - captured: List[httpx.Request], + captured: list[httpx.Request], ) -> AsyncDial: client = AsyncDial(api_key="dummy", base_url="http://dial.core") @@ -79,11 +79,6 @@ async def send_mock(request: httpx.Request, **_: Any) -> httpx.Response: return client -# --------------------------------------------------------------------------- -# prompts.get() -# --------------------------------------------------------------------------- - - def test_get_prompt(): client = get_client_mock(status_code=200, json_mock=PROMPT_MOCK) result = client.prompts.get("prompts/test-bucket/my-folder/my-prompt") @@ -170,11 +165,6 @@ async def test_async_get_prompt_http_error(): await client.prompts.get("prompts/test-bucket/my-folder/my-prompt") -# --------------------------------------------------------------------------- -# prompts.get_metadata() -# --------------------------------------------------------------------------- - - def test_get_prompt_metadata(): client = get_client_mock(status_code=200, json_mock=PROMPT_METADATA_MOCK) result = client.prompts.get_metadata( @@ -198,11 +188,6 @@ async def test_async_get_prompt_metadata(): assert result.bucket == "test-bucket" -# --------------------------------------------------------------------------- -# prompts.save() -# --------------------------------------------------------------------------- - - def test_save_prompt(): client = get_client_mock(status_code=200, json_mock=PROMPT_METADATA_MOCK) prompt = Prompt(**PROMPT_MOCK) @@ -233,7 +218,7 @@ async def test_async_save_prompt(): def test_save_prompt_sends_json_and_etag_headers(): - captured: List[httpx.Request] = [] + captured: list[httpx.Request] = [] client = _make_capturing_client(captured) prompt = Prompt(**PROMPT_MOCK) @@ -261,7 +246,7 @@ def test_save_prompt_sends_json_and_etag_headers(): @pytest.mark.asyncio async def test_async_save_prompt_sends_json_and_etag_headers(): - captured: List[httpx.Request] = [] + captured: list[httpx.Request] = [] client = _make_async_capturing_client(captured) prompt = Prompt(**PROMPT_MOCK) @@ -349,11 +334,6 @@ async def test_async_save_prompt_rejects_non_prompt_url(): ) -# --------------------------------------------------------------------------- -# prompts.delete() -# --------------------------------------------------------------------------- - - def test_delete_prompt(): client = get_client_mock(status_code=200, json_mock={}) @@ -372,7 +352,7 @@ async def test_async_delete_prompt(): def test_delete_prompt_sends_etag_header(): - captured: List[httpx.Request] = [] + captured: list[httpx.Request] = [] client = _make_capturing_client(captured) result = client.prompts.delete( @@ -390,7 +370,7 @@ def test_delete_prompt_sends_etag_header(): @pytest.mark.asyncio async def test_async_delete_prompt_sends_etag_header(): - captured: List[httpx.Request] = [] + captured: list[httpx.Request] = [] client = _make_async_capturing_client(captured) result = await client.prompts.delete( diff --git a/tests/test_auth.py b/tests/test_auth.py index f4877c6..f068b15 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -158,7 +158,7 @@ def test_combined_auth(): """Test that both api_key and bearer_token can be provided simultaneously.""" client = Dial( api_key="my-api-key", - bearer_token="my-bearer-token", + bearer_token="my-bearer-token", # noqa: S106 base_url="http://dial.core", ) headers = client.auth_headers() @@ -173,7 +173,7 @@ async def test_combined_auth_async(): """Test that both api_key and bearer_token can be provided simultaneously in an async client.""" client = AsyncDial( api_key="my-api-key", - bearer_token="my-bearer-token", + bearer_token="my-bearer-token", # noqa: S106 base_url="http://dial.core", ) headers = await client.auth_headers() diff --git a/tests/utils/chunks.py b/tests/utils/chunks.py index eee4ca9..7434da1 100644 --- a/tests/utils/chunks.py +++ b/tests/utils/chunks.py @@ -1,12 +1,11 @@ import json -from typing import Optional, Union def create_mock_chunk( *, - delta: Optional[dict] = None, - finish_reason: Optional[str] = None, - usage: Optional[dict] = None, + delta: dict | None = None, + finish_reason: str | None = None, + usage: dict | None = None, ) -> dict: return { "id": "chatcmpl-test", @@ -26,9 +25,6 @@ def create_mock_chunk( } -def create_sse_data_field(chunk: Union[dict, str]) -> bytes: - if isinstance(chunk, dict): - s = json.dumps(chunk) - else: - s = chunk +def create_sse_data_field(chunk: dict | str) -> bytes: + s = json.dumps(chunk) if isinstance(chunk, dict) else chunk return f"data: {s}\n\n".encode()