From a7249779cc6988abc9060c5f7887f47099c1e3c9 Mon Sep 17 00:00:00 2001 From: Alyssa Coghlan Date: Fri, 25 Apr 2025 02:01:42 +1000 Subject: [PATCH] Hide the not-yet-implemented file handling APIs When the image preparation APIs were added to the server API, the general purpose file preparation APIs were also incorrectly marked as public in the client SDK API. Having those APIs available suggests that adding other file types (text files, PDFs, etc) to the chat context via the SDK is expected to work, when that simply isn't true (yet). --- src/lmstudio/async_api.py | 12 +++++++----- src/lmstudio/history.py | 7 ++++--- src/lmstudio/sync_api.py | 20 +++++++++++--------- tests/async/test_images_async.py | 6 +++--- tests/sync/test_images_sync.py | 6 +++--- tests/test_convenience_api.py | 2 +- tests/test_history.py | 2 +- 7 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/lmstudio/async_api.py b/src/lmstudio/async_api.py index 3f8e119..41fdc70 100644 --- a/src/lmstudio/async_api.py +++ b/src/lmstudio/async_api.py @@ -609,8 +609,9 @@ async def _fetch_file_handle(self, file_data: _LocalFileData) -> FileHandle: handle["type"] = "file" return load_struct(handle, FileHandle) - @sdk_public_api_async() - async def prepare_file( + # Not yet implemented (server API only supports the same file types as prepare_image) + # @sdk_public_api_async() + async def _prepare_file( self, src: LocalFileInput, name: str | None = None ) -> FileHandle: """Add a file to the server. Returns a file handle for use in prediction requests.""" @@ -1435,12 +1436,13 @@ def repository(self) -> AsyncSessionRepository: return self._get_session(AsyncSessionRepository) # Convenience methods - @sdk_public_api_async() - async def prepare_file( + # Not yet implemented (server API only supports the same file types as prepare_image) + # @sdk_public_api_async() + async def _prepare_file( self, src: LocalFileInput, name: str | None = None ) -> FileHandle: """Add a file to the server. Returns a file handle for use in prediction requests.""" - return await self.files.prepare_file(src, name) + return await self.files._prepare_file(src, name) @sdk_public_api_async() async def prepare_image( diff --git a/src/lmstudio/history.py b/src/lmstudio/history.py index 8445c2d..0d087dc 100644 --- a/src/lmstudio/history.py +++ b/src/lmstudio/history.py @@ -374,8 +374,9 @@ def add_user_message( self, content: UserMessageInput | Iterable[UserMessageInput], *, - files: Sequence[FileHandleInput] = (), images: Sequence[FileHandleInput] = (), + # Not yet implemented (server file preparation API only supports the image file types) + _files: Sequence[FileHandleInput] = (), ) -> UserMessage: """Add a new user message to the chat history.""" # Accept both singular and multi-part user messages @@ -385,8 +386,8 @@ def add_user_message( else: content_items = list(content) # Convert given local file information to file handles - if files: - content_items.extend(files) + if _files: + content_items.extend(_files) if images: content_items.extend(images) # Consecutive messages with the same role are not supported, diff --git a/src/lmstudio/sync_api.py b/src/lmstudio/sync_api.py index 7b382bf..3d87dbf 100644 --- a/src/lmstudio/sync_api.py +++ b/src/lmstudio/sync_api.py @@ -144,7 +144,6 @@ "list_downloaded_models", "list_loaded_models", "llm", - "prepare_file", "prepare_image", ] @@ -586,8 +585,9 @@ def _fetch_file_handle(self, file_data: _LocalFileData) -> FileHandle: handle["type"] = "file" return load_struct(handle, FileHandle) - @sdk_public_api() - def prepare_file(self, src: LocalFileInput, name: str | None = None) -> FileHandle: + # Not yet implemented (server API only supports the same file types as prepare_image) + # @sdk_public_api() + def _prepare_file(self, src: LocalFileInput, name: str | None = None) -> FileHandle: """Add a file to the server. Returns a file handle for use in prediction requests.""" file_data = _LocalFileData(src, name) return self._fetch_file_handle(file_data) @@ -1568,10 +1568,11 @@ def repository(self) -> SyncSessionRepository: return self._get_session(SyncSessionRepository) # Convenience methods - @sdk_public_api() - def prepare_file(self, src: LocalFileInput, name: str | None = None) -> FileHandle: + # Not yet implemented (server API only supports the same file types as prepare_image) + # @sdk_public_api() + def _prepare_file(self, src: LocalFileInput, name: str | None = None) -> FileHandle: """Add a file to the server. Returns a file handle for use in prediction requests.""" - return self.files.prepare_file(src, name) + return self.files._prepare_file(src, name) @sdk_public_api() def prepare_image(self, src: LocalFileInput, name: str | None = None) -> FileHandle: @@ -1653,10 +1654,11 @@ def embedding_model( return get_default_client().embedding.model(model_key, ttl=ttl, config=config) -@sdk_public_api() -def prepare_file(src: LocalFileInput, name: str | None = None) -> FileHandle: +# Not yet implemented (server API only supports the same file types as prepare_image) +# @sdk_public_api() +def _prepare_file(src: LocalFileInput, name: str | None = None) -> FileHandle: """Add a file to the server using the default global client.""" - return get_default_client().prepare_file(src, name) + return get_default_client()._prepare_file(src, name) @sdk_public_api() diff --git a/tests/async/test_images_async.py b/tests/async/test_images_async.py index f833cda..8535076 100644 --- a/tests/async/test_images_async.py +++ b/tests/async/test_images_async.py @@ -24,7 +24,7 @@ async def test_upload_from_pathlike_async(caplog: LogCap) -> None: caplog.set_level(logging.DEBUG) async with AsyncClient() as client: session = client.files - file = await session.prepare_file(IMAGE_FILEPATH) + file = await session._prepare_file(IMAGE_FILEPATH) assert file assert isinstance(file, FileHandle) logging.info(f"Uploaded file: {file}") @@ -43,7 +43,7 @@ async def test_upload_from_file_obj_async(caplog: LogCap) -> None: async with AsyncClient() as client: session = client.files with open(IMAGE_FILEPATH, "rb") as f: - file = await session.prepare_file(f) + file = await session._prepare_file(f) assert file assert isinstance(file, FileHandle) logging.info(f"Uploaded file: {file}") @@ -62,7 +62,7 @@ async def test_upload_from_bytesio_async(caplog: LogCap) -> None: caplog.set_level(logging.DEBUG) async with AsyncClient() as client: session = client.files - file = await session.prepare_file(BytesIO(IMAGE_FILEPATH.read_bytes())) + file = await session._prepare_file(BytesIO(IMAGE_FILEPATH.read_bytes())) assert file assert isinstance(file, FileHandle) logging.info(f"Uploaded file: {file}") diff --git a/tests/sync/test_images_sync.py b/tests/sync/test_images_sync.py index 0943e2c..b944b40 100644 --- a/tests/sync/test_images_sync.py +++ b/tests/sync/test_images_sync.py @@ -30,7 +30,7 @@ def test_upload_from_pathlike_sync(caplog: LogCap) -> None: caplog.set_level(logging.DEBUG) with Client() as client: session = client.files - file = session.prepare_file(IMAGE_FILEPATH) + file = session._prepare_file(IMAGE_FILEPATH) assert file assert isinstance(file, FileHandle) logging.info(f"Uploaded file: {file}") @@ -48,7 +48,7 @@ def test_upload_from_file_obj_sync(caplog: LogCap) -> None: with Client() as client: session = client.files with open(IMAGE_FILEPATH, "rb") as f: - file = session.prepare_file(f) + file = session._prepare_file(f) assert file assert isinstance(file, FileHandle) logging.info(f"Uploaded file: {file}") @@ -66,7 +66,7 @@ def test_upload_from_bytesio_sync(caplog: LogCap) -> None: caplog.set_level(logging.DEBUG) with Client() as client: session = client.files - file = session.prepare_file(BytesIO(IMAGE_FILEPATH.read_bytes())) + file = session._prepare_file(BytesIO(IMAGE_FILEPATH.read_bytes())) assert file assert isinstance(file, FileHandle) logging.info(f"Uploaded file: {file}") diff --git a/tests/test_convenience_api.py b/tests/test_convenience_api.py index e205789..c2bc031 100644 --- a/tests/test_convenience_api.py +++ b/tests/test_convenience_api.py @@ -51,7 +51,7 @@ def test_embedding_specific() -> None: def test_prepare_file() -> None: name = "example-file.txt" raw_data = b"raw data" - file_handle = lms.prepare_file(raw_data, name) + file_handle = lms.sync_api._prepare_file(raw_data, name) assert file_handle.name == name assert file_handle.size_bytes == len(raw_data) assert file_handle.file_type == "text/plain" diff --git a/tests/test_history.py b/tests/test_history.py index 902588b..6ec7925 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -624,7 +624,7 @@ def test_user_message_attachments() -> None: chat.add_user_message( "What do you make of this?", images=[INPUT_IMAGE_HANDLE], - files=[INPUT_FILE_HANDLE], + _files=[INPUT_FILE_HANDLE], ) history = chat._get_history() assert history["messages"] == EXPECTED_USER_ATTACHMENT_MESSAGES