diff --git a/pyproject.toml b/pyproject.toml index 1fad8a6..edbe330 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,7 +97,6 @@ markers = [ # Warnings should only be emitted when being specifically tested filterwarnings = [ "error", - "ignore:.*the async API is not yet stable:FutureWarning" ] # Capture log info from network client libraries log_format = "%(asctime)s %(levelname)s %(message)s" diff --git a/src/lmstudio/_ws_impl.py b/src/lmstudio/_ws_impl.py index 80dba6f..2565f29 100644 --- a/src/lmstudio/_ws_impl.py +++ b/src/lmstudio/_ws_impl.py @@ -11,7 +11,10 @@ import asyncio -from concurrent.futures import Future as SyncFuture + +# Python 3.10 compatibility: use concurrent.futures.TimeoutError instead of the builtin +# In 3.11+, these are the same type, in 3.10 futures have their own timeout exception +from concurrent.futures import Future as SyncFuture, TimeoutError as SyncFutureTimeout from contextlib import AsyncExitStack, contextmanager from functools import partial from typing import ( @@ -47,6 +50,12 @@ # and omits the generalised features that the SDK doesn't need) T = TypeVar("T") +__all__ = [ + "SyncFutureTimeout", + "AsyncTaskManager", + "AsyncWebsocketHandler", +] + class AsyncTaskManager: def __init__(self, *, on_activation: Callable[[], Any] | None = None) -> None: @@ -429,7 +438,7 @@ def _rx_queue_get_threadsafe(self, rx_queue: RxQueue, timeout: float | None) -> future = self._task_manager.run_coroutine_threadsafe(rx_queue.get()) try: return future.result(timeout) - except TimeoutError: + except SyncFutureTimeout: future.cancel() raise diff --git a/src/lmstudio/async_api.py b/src/lmstudio/async_api.py index 25605a4..ffaedcd 100644 --- a/src/lmstudio/async_api.py +++ b/src/lmstudio/async_api.py @@ -1,7 +1,6 @@ """Async I/O protocol implementation for the LM Studio remote access API.""" import asyncio -import warnings from abc import abstractmethod from contextlib import AsyncExitStack, asynccontextmanager @@ -109,8 +108,8 @@ # and similar tasks is published from `json_api`. # Bypassing the high level API, and working more # directly with the underlying websocket(s) is -# supported (hence the public names), but they're -# not exported via the top-level `lmstudio` API. +# not supported due to the complexity of the task +# management details (hence the private names). __all__ = [ "AnyAsyncDownloadedModel", "AsyncClient", @@ -215,7 +214,7 @@ async def receive_result(self) -> Any: return self._rpc.handle_rx_message(message) -class AsyncLMStudioWebsocket(LMStudioWebsocket[AsyncWebSocketSession]): +class _AsyncLMStudioWebsocket(LMStudioWebsocket[AsyncWebSocketSession]): """Asynchronous websocket client that handles demultiplexing of reply messages.""" def __init__( @@ -331,7 +330,7 @@ async def remote_call( return await rpc.receive_result() -class AsyncSession(ClientSession["AsyncClient", AsyncLMStudioWebsocket]): +class _AsyncSession(ClientSession["AsyncClient", _AsyncLMStudioWebsocket]): """Async client session interfaces applicable to all API namespaces.""" def __init__(self, client: "AsyncClient") -> None: @@ -354,7 +353,7 @@ async def __aexit__(self, *args: Any) -> None: await self.disconnect() @sdk_public_api_async() - async def connect(self) -> AsyncLMStudioWebsocket: + async def connect(self) -> _AsyncLMStudioWebsocket: """Connect the client session.""" self._fail_if_connected("Attempted to connect already connected session") api_host = self._client.api_host @@ -367,7 +366,7 @@ async def connect(self) -> AsyncLMStudioWebsocket: resources = self._resource_manager client = self._client self._lmsws = lmsws = await resources.enter_async_context( - AsyncLMStudioWebsocket( + _AsyncLMStudioWebsocket( client._task_manager, session_url, client._auth_details ) ) @@ -411,7 +410,7 @@ async def remote_call( TAsyncSessionModel = TypeVar( - "TAsyncSessionModel", bound="AsyncSessionModel[Any, Any, Any, Any]" + "TAsyncSessionModel", bound="_AsyncSessionModel[Any, Any, Any, Any]" ) TAsyncModelHandle = TypeVar("TAsyncModelHandle", bound="AsyncModelHandle[Any]") @@ -467,7 +466,7 @@ async def model( class AsyncDownloadedEmbeddingModel( AsyncDownloadedModel[ EmbeddingModelInfo, - "AsyncSessionEmbedding", + "_AsyncSessionEmbedding", EmbeddingLoadModelConfig, EmbeddingLoadModelConfigDict, "AsyncEmbeddingModel", @@ -476,7 +475,7 @@ class AsyncDownloadedEmbeddingModel( """Asynchronous download listing for an embedding model.""" def __init__( - self, model_info: DictObject, session: "AsyncSessionEmbedding" + self, model_info: DictObject, session: "_AsyncSessionEmbedding" ) -> None: """Initialize downloaded embedding model details.""" super().__init__(EmbeddingModelInfo, model_info, session) @@ -485,7 +484,7 @@ def __init__( class AsyncDownloadedLlm( AsyncDownloadedModel[ LlmInfo, - "AsyncSessionLlm", + "_AsyncSessionLlm", LlmLoadModelConfig, LlmLoadModelConfigDict, "AsyncLLM", @@ -493,7 +492,7 @@ class AsyncDownloadedLlm( ): """Asynchronous ownload listing for an LLM.""" - def __init__(self, model_info: DictObject, session: "AsyncSessionLlm") -> None: + def __init__(self, model_info: DictObject, session: "_AsyncSessionLlm") -> None: """Initialize downloaded embedding model details.""" super().__init__(LlmInfo, model_info, session) @@ -501,7 +500,7 @@ def __init__(self, model_info: DictObject, session: "AsyncSessionLlm") -> None: AnyAsyncDownloadedModel: TypeAlias = AsyncDownloadedModel[Any, Any, Any, Any, Any] -class AsyncSessionSystem(AsyncSession): +class _AsyncSessionSystem(_AsyncSession): """Async client session for the system namespace.""" API_NAMESPACE = "system" @@ -531,7 +530,7 @@ def _process_download_listing( ) -class _AsyncSessionFiles(AsyncSession): +class _AsyncSessionFiles(_AsyncSession): """Async client session for the files namespace.""" API_NAMESPACE = "files" @@ -562,7 +561,7 @@ async def prepare_image( return await self._fetch_file_handle(file_data) -class AsyncModelDownloadOption(ModelDownloadOptionBase[AsyncSession]): +class AsyncModelDownloadOption(ModelDownloadOptionBase[_AsyncSession]): """A single download option for a model search result.""" @sdk_public_api_async() @@ -577,10 +576,10 @@ async def download( return await channel.wait_for_result() -class AsyncAvailableModel(AvailableModelBase[AsyncSession]): +class AsyncAvailableModel(AvailableModelBase[_AsyncSession]): """A model available for download from the model repository.""" - _session: AsyncSession + _session: _AsyncSession @sdk_public_api_async() async def get_download_options( @@ -595,7 +594,7 @@ async def get_download_options( return final -class AsyncSessionRepository(AsyncSession): +class _AsyncSessionRepository(_AsyncSession): """Async client session for the repository namespace.""" API_NAMESPACE = "repository" @@ -616,8 +615,8 @@ async def search_models( TAsyncDownloadedModel = TypeVar("TAsyncDownloadedModel", bound=AnyAsyncDownloadedModel) -class AsyncSessionModel( - AsyncSession, +class _AsyncSessionModel( + _AsyncSession, Generic[ TAsyncModelHandle, TLoadConfig, @@ -630,7 +629,7 @@ class AsyncSessionModel( _API_TYPES: Type[ModelSessionTypes[TLoadConfig]] @property - def _system_session(self) -> AsyncSessionSystem: + def _system_session(self) -> _AsyncSessionSystem: return self._client.system @property @@ -922,8 +921,8 @@ async def cancel(self) -> None: await self._channel.cancel() -class AsyncSessionLlm( - AsyncSessionModel[ +class _AsyncSessionLlm( + _AsyncSessionModel[ "AsyncLLM", LlmLoadModelConfig, LlmLoadModelConfigDict, @@ -1028,8 +1027,8 @@ async def _apply_prompt_template( return response.get("formatted", "") if response else "" -class AsyncSessionEmbedding( - AsyncSessionModel[ +class _AsyncSessionEmbedding( + _AsyncSessionModel[ "AsyncEmbeddingModel", EmbeddingLoadModelConfig, EmbeddingLoadModelConfigDict, @@ -1115,7 +1114,7 @@ async def get_context_length(self) -> int: AnyAsyncModel: TypeAlias = AsyncModelHandle[Any] -class AsyncLLM(AsyncModelHandle[AsyncSessionLlm]): +class AsyncLLM(AsyncModelHandle[_AsyncSessionLlm]): """Reference to a loaded LLM model.""" @sdk_public_api_async() @@ -1258,7 +1257,7 @@ async def apply_prompt_template( ) -class AsyncEmbeddingModel(AsyncModelHandle[AsyncSessionEmbedding]): +class AsyncEmbeddingModel(AsyncModelHandle[_AsyncSessionEmbedding]): """Reference to a loaded embedding model.""" # Alas, type hints don't properly support distinguishing str vs Iterable[str]: @@ -1271,11 +1270,7 @@ async def embed( return await self._session._embed(self.identifier, input) -TAsyncSession = TypeVar("TAsyncSession", bound=AsyncSession) - -_ASYNC_API_STABILITY_WARNING = """\ -Note the async API is not yet stable and is expected to change in future releases -""" +TAsyncSession = TypeVar("TAsyncSession", bound=_AsyncSession) class AsyncClient(ClientBase): @@ -1283,12 +1278,9 @@ class AsyncClient(ClientBase): def __init__(self, api_host: str | None = None) -> None: """Initialize API client.""" - # Warn about the async API stability, since we expect it to change - # (in particular, accepting coroutine functions as callbacks) - warnings.warn(_ASYNC_API_STABILITY_WARNING, FutureWarning) super().__init__(api_host) self._resources = AsyncExitStack() - self._sessions: dict[str, AsyncSession] = {} + self._sessions: dict[str, _AsyncSession] = {} self._task_manager = AsyncTaskManager() # Unlike the sync API, we don't support GC-based resource # management in the async API. Structured concurrency @@ -1301,12 +1293,12 @@ def __init__(self, api_host: str | None = None) -> None: # TODO: revisit lazy connections given the task manager implementation # (for example, eagerly start tasks for all sessions, and lazily # trigger events that allow them to initiate their connection) - _ALL_SESSIONS: tuple[Type[AsyncSession], ...] = ( - AsyncSessionEmbedding, + _ALL_SESSIONS: tuple[Type[_AsyncSession], ...] = ( + _AsyncSessionEmbedding, _AsyncSessionFiles, - AsyncSessionLlm, - AsyncSessionRepository, - AsyncSessionSystem, + _AsyncSessionLlm, + _AsyncSessionRepository, + _AsyncSessionSystem, ) async def __aenter__(self) -> Self: @@ -1342,20 +1334,20 @@ def _get_session(self, cls: Type[TAsyncSession]) -> TAsyncSession: @property @sdk_public_api() - def llm(self) -> AsyncSessionLlm: + def llm(self) -> _AsyncSessionLlm: """Return the LLM API client session.""" - return self._get_session(AsyncSessionLlm) + return self._get_session(_AsyncSessionLlm) @property @sdk_public_api() - def embedding(self) -> AsyncSessionEmbedding: + def embedding(self) -> _AsyncSessionEmbedding: """Return the embedding model API client session.""" - return self._get_session(AsyncSessionEmbedding) + return self._get_session(_AsyncSessionEmbedding) @property - def system(self) -> AsyncSessionSystem: + def system(self) -> _AsyncSessionSystem: """Return the system API client session.""" - return self._get_session(AsyncSessionSystem) + return self._get_session(_AsyncSessionSystem) @property def files(self) -> _AsyncSessionFiles: @@ -1363,9 +1355,9 @@ def files(self) -> _AsyncSessionFiles: return self._get_session(_AsyncSessionFiles) @property - def repository(self) -> AsyncSessionRepository: + def repository(self) -> _AsyncSessionRepository: """Return the repository API client session.""" - return self._get_session(AsyncSessionRepository) + return self._get_session(_AsyncSessionRepository) # Convenience methods # Not yet implemented (server API only supports the same file types as prepare_image) diff --git a/src/lmstudio/plugin/cli.py b/src/lmstudio/plugin/cli.py index a528528..5a7a9fe 100644 --- a/src/lmstudio/plugin/cli.py +++ b/src/lmstudio/plugin/cli.py @@ -44,9 +44,6 @@ def main(argv: Sequence[str] | None = None) -> int: warnings.filterwarnings( "ignore", ".*the plugin API is not yet stable", FutureWarning ) - warnings.filterwarnings( - "ignore", ".*the async API is not yet stable", FutureWarning - ) log_level = logging.DEBUG if args.debug else logging.INFO logging.basicConfig(level=log_level) if not args.dev: diff --git a/src/lmstudio/plugin/hooks/common.py b/src/lmstudio/plugin/hooks/common.py index 8ed4d35..80289d0 100644 --- a/src/lmstudio/plugin/hooks/common.py +++ b/src/lmstudio/plugin/hooks/common.py @@ -18,7 +18,7 @@ from anyio import move_on_after -from ...async_api import AsyncSession +from ...async_api import _AsyncSession from ...schemas import DictObject from ..._sdk_models import ( # TODO: Define aliases at schema generation time @@ -32,13 +32,13 @@ # Available as lmstudio.plugin.hooks.* __all__ = [ - "AsyncSessionPlugins", + "_AsyncSessionPlugins", "TPluginConfigSchema", "TGlobalConfigSchema", ] -class AsyncSessionPlugins(AsyncSession): +class _AsyncSessionPlugins(_AsyncSession): """Async client session for the plugins namespace.""" API_NAMESPACE = "plugins" @@ -63,7 +63,7 @@ class HookController(Generic[TPluginRequest, TPluginConfigSchema, TGlobalConfigS def __init__( self, - session: AsyncSessionPlugins, + session: _AsyncSessionPlugins, request: TPluginRequest, plugin_config_schema: type[TPluginConfigSchema], global_config_schema: type[TGlobalConfigSchema], diff --git a/src/lmstudio/plugin/hooks/prompt_preprocessor.py b/src/lmstudio/plugin/hooks/prompt_preprocessor.py index 73a4910..a1a3175 100644 --- a/src/lmstudio/plugin/hooks/prompt_preprocessor.py +++ b/src/lmstudio/plugin/hooks/prompt_preprocessor.py @@ -47,7 +47,7 @@ ) from ..config_schemas import BaseConfigSchema from .common import ( - AsyncSessionPlugins, + _AsyncSessionPlugins, HookController, SendMessageCallback, ServerRequestError, @@ -129,7 +129,7 @@ class PromptPreprocessorController( def __init__( self, - session: AsyncSessionPlugins, + session: _AsyncSessionPlugins, request: PromptPreprocessingRequest, plugin_config_schema: type[TPluginConfigSchema], global_config_schema: type[TGlobalConfigSchema], @@ -210,7 +210,7 @@ def __post_init__(self) -> None: self._abort_events: dict[str, asyncio.Event] = {} async def process_requests( - self, session: AsyncSessionPlugins, notify_ready: Callable[[], Any] + self, session: _AsyncSessionPlugins, notify_ready: Callable[[], Any] ) -> None: """Create plugin channel and wait for server requests.""" logger = self._logger @@ -376,7 +376,7 @@ async def run_prompt_preprocessor( hook_impl: PromptPreprocessorHook, plugin_config_schema: type[BaseConfigSchema], global_config_schema: type[BaseConfigSchema], - session: AsyncSessionPlugins, + session: _AsyncSessionPlugins, notify_ready: Callable[[], Any], ) -> None: """Accept prompt preprocessing requests.""" diff --git a/src/lmstudio/plugin/hooks/token_generator.py b/src/lmstudio/plugin/hooks/token_generator.py index 51bb5ce..76d53b8 100644 --- a/src/lmstudio/plugin/hooks/token_generator.py +++ b/src/lmstudio/plugin/hooks/token_generator.py @@ -8,7 +8,7 @@ from ..config_schemas import BaseConfigSchema from .common import ( - AsyncSessionPlugins, + _AsyncSessionPlugins, HookController, TPluginConfigSchema, TGlobalConfigSchema, @@ -36,7 +36,7 @@ async def run_token_generator( hook_impl: TokenGeneratorHook, plugin_config_schema: type[BaseConfigSchema], global_config_schema: type[BaseConfigSchema], - session: AsyncSessionPlugins, + session: _AsyncSessionPlugins, notify_ready: Callable[[], Any], ) -> None: """Accept token generation requests.""" diff --git a/src/lmstudio/plugin/hooks/tools_provider.py b/src/lmstudio/plugin/hooks/tools_provider.py index e43b096..67d2e3b 100644 --- a/src/lmstudio/plugin/hooks/tools_provider.py +++ b/src/lmstudio/plugin/hooks/tools_provider.py @@ -13,7 +13,7 @@ from ..config_schemas import BaseConfigSchema from .common import ( - AsyncSessionPlugins, + _AsyncSessionPlugins, HookController, TPluginConfigSchema, TGlobalConfigSchema, @@ -43,7 +43,7 @@ async def run_tools_provider( hook_impl: ToolsProviderHook, plugin_config_schema: type[BaseConfigSchema], global_config_schema: type[BaseConfigSchema], - session: AsyncSessionPlugins, + session: _AsyncSessionPlugins, notify_ready: Callable[[], Any], ) -> None: """Accept tools provider session requests.""" diff --git a/src/lmstudio/plugin/runner.py b/src/lmstudio/plugin/runner.py index fa81ba7..8511a65 100644 --- a/src/lmstudio/plugin/runner.py +++ b/src/lmstudio/plugin/runner.py @@ -27,7 +27,7 @@ from .sdk_api import LMStudioPluginInitError, LMStudioPluginRuntimeError from .config_schemas import BaseConfigSchema from .hooks import ( - AsyncSessionPlugins, + _AsyncSessionPlugins, TPluginConfigSchema, TGlobalConfigSchema, run_prompt_preprocessor, @@ -55,7 +55,7 @@ THookImpl, type[TPluginConfigSchema], type[TGlobalConfigSchema], - AsyncSessionPlugins, + _AsyncSessionPlugins, ReadyCallback, ], Awaitable[Any], @@ -100,7 +100,7 @@ def __init__( _ALL_SESSIONS = ( # Plugin controller access all runs through a dedicated endpoint - AsyncSessionPlugins, + _AsyncSessionPlugins, ) def _create_auth_message(self) -> DictObject: @@ -111,9 +111,9 @@ def _create_auth_message(self) -> DictObject: return self._format_auth_message(self._client_id, self._client_key) @property - def plugins(self) -> AsyncSessionPlugins: + def plugins(self) -> _AsyncSessionPlugins: """Return the plugins API client session.""" - return self._get_session(AsyncSessionPlugins) + return self._get_session(_AsyncSessionPlugins) async def _run_hook_impl( self, diff --git a/src/lmstudio/sync_api.py b/src/lmstudio/sync_api.py index b168eb8..c4c9ae6 100644 --- a/src/lmstudio/sync_api.py +++ b/src/lmstudio/sync_api.py @@ -110,6 +110,7 @@ _model_spec_to_api_dict, _redact_json, ) +from ._ws_impl import SyncFutureTimeout from ._ws_thread import AsyncWebsocketThread, SyncToAsyncWebsocketBridge from ._kv_config import TLoadConfig, TLoadConfigDict, parse_server_config from ._sdk_models import ( @@ -228,7 +229,7 @@ def rx_stream( # (we can't easily suppress the SDK's own frames for iterators) try: message = self._get_message(self.timeout) - except TimeoutError: + except SyncFutureTimeout: raise LMStudioTimeoutError from None if message is None: raise LMStudioWebsocketError("Client unexpectedly disconnected.") @@ -284,7 +285,7 @@ def receive_result(self) -> Any: """Receive call response on the receive queue.""" try: message = self._get_message(self.timeout) - except TimeoutError: + except SyncFutureTimeout: raise LMStudioTimeoutError from None if message is None: raise LMStudioWebsocketError("Client unexpectedly disconnected.") diff --git a/tests/test_session_errors.py b/tests/test_session_errors.py index ec15b3e..f4e3e2a 100644 --- a/tests/test_session_errors.py +++ b/tests/test_session_errors.py @@ -13,8 +13,8 @@ Client, ) from lmstudio.async_api import ( - AsyncSession, - AsyncSessionSystem, + _AsyncSession, + _AsyncSessionSystem, ) from lmstudio.sync_api import ( SyncLMStudioWebsocket, @@ -34,7 +34,7 @@ from .support.lmstudio import ErrFunc -async def check_call_errors_async(session: AsyncSession) -> None: +async def check_call_errors_async(session: _AsyncSession) -> None: # Remote calls on the underlying websocket are expected to fail when not connected with pytest.raises( LMStudioWebsocketError, @@ -57,7 +57,7 @@ async def check_call_errors_async(session: AsyncSession) -> None: @pytest.mark.asyncio async def test_session_not_started_async(caplog: LogCap) -> None: caplog.set_level(logging.DEBUG) - session = AsyncSessionSystem(AsyncClient()) + session = _AsyncSessionSystem(AsyncClient()) # Sessions start out disconnected assert not session.connected # Check server call errors are reported as expected @@ -69,7 +69,7 @@ async def test_session_not_started_async(caplog: LogCap) -> None: async def test_session_disconnected_async(caplog: LogCap) -> None: caplog.set_level(logging.DEBUG) client = AsyncClient() - session = AsyncSessionSystem(client) + session = _AsyncSessionSystem(client) async with client._task_manager, session: assert session.connected # Session is disconnected after use @@ -82,7 +82,7 @@ async def test_session_disconnected_async(caplog: LogCap) -> None: async def test_session_closed_port_async(caplog: LogCap) -> None: caplog.set_level(logging.DEBUG) client = AsyncClient(closed_api_host()) - session = AsyncSessionSystem(client) + session = _AsyncSessionSystem(client) # Sessions start out disconnected assert not session.connected # Should get an SDK exception rather than the underlying exception @@ -101,7 +101,7 @@ async def test_session_nonresponsive_port_async(caplog: LogCap) -> None: caplog.set_level(logging.DEBUG) with nonresponsive_api_host() as api_host: client = AsyncClient(api_host) - session = AsyncSessionSystem(client) + session = _AsyncSessionSystem(client) # Sessions start out disconnected assert not session.connected # Should get an SDK exception rather than the underlying exception diff --git a/tests/test_sessions.py b/tests/test_sessions.py index 9dce3c1..e93c87c 100644 --- a/tests/test_sessions.py +++ b/tests/test_sessions.py @@ -12,9 +12,9 @@ LMStudioWebsocketError, ) from lmstudio.async_api import ( - AsyncLMStudioWebsocket, - AsyncSession, - AsyncSessionSystem, + _AsyncLMStudioWebsocket, + _AsyncSession, + _AsyncSessionSystem, ) from lmstudio.sync_api import ( SyncLMStudioWebsocket, @@ -27,7 +27,7 @@ from .support import LOCAL_API_HOST -async def check_connected_async_session(session: AsyncSession) -> None: +async def check_connected_async_session(session: _AsyncSession) -> None: assert session.connected session_ws = session._lmsws assert session_ws is not None @@ -50,7 +50,7 @@ async def check_connected_async_session(session: AsyncSession) -> None: async def test_session_cm_async(caplog: LogCap) -> None: caplog.set_level(logging.DEBUG) client = AsyncClient() - session = AsyncSessionSystem(client) + session = _AsyncSessionSystem(client) # Sessions start out disconnected assert not session.connected # Disconnecting should run without error @@ -156,7 +156,7 @@ async def test_websocket_cm_async(caplog: LogCap) -> None: caplog.set_level(logging.DEBUG) auth_details = AsyncClient._format_auth_message() tm = AsyncTaskManager(on_activation=None) - lmsws = AsyncLMStudioWebsocket(tm, f"http://{LOCAL_API_HOST}/system", auth_details) + lmsws = _AsyncLMStudioWebsocket(tm, f"http://{LOCAL_API_HOST}/system", auth_details) # SDK client websockets start out disconnected assert not lmsws.connected # Entering the CM opens the websocket if it isn't already open diff --git a/tests/unload_models.py b/tests/unload_models.py index 675651e..d8fcdfd 100644 --- a/tests/unload_models.py +++ b/tests/unload_models.py @@ -10,7 +10,9 @@ TOOL_LLM_ID, ) -AsyncSessionModel = lms.async_api.AsyncSessionEmbedding | lms.async_api.AsyncSessionLlm +AsyncSessionModel = ( + lms.async_api._AsyncSessionEmbedding | lms.async_api._AsyncSessionLlm +) async def _unload_model(session: AsyncSessionModel, model_identifier: str) -> None: diff --git a/tox.ini b/tox.ini index 642c9cb..d605fba 100644 --- a/tox.ini +++ b/tox.ini @@ -25,11 +25,11 @@ commands = [testenv:load-test-models] commands = - python -W "ignore:Note the async API is not yet stable:FutureWarning" -m tests.load_models + python -m tests.load_models [testenv:unload-test-models] commands = - python -W "ignore:Note the async API is not yet stable:FutureWarning" -m tests.unload_models + python -m tests.unload_models [testenv:coverage] # Subprocess coverage based on https://hynek.me/articles/turbo-charge-tox/