Skip to content

Commit 96a388e

Browse files
CrossPr0ductclaude
andcommitted
refactor: optimize file handling to avoid redundant buffer conversions
- Change _get_file_details() to return Buffer instead of bytes - Add special handling for memoryview inputs to avoid unnecessary conversion - Support all buffer protocol objects (bytes, bytearray, memoryview, array.array, etc.) - Update type annotations throughout to use Buffer type from typing_extensions - Improve error messages to mention "buffer" instead of just "bytes" This change reduces memory overhead when working with buffer protocol objects by preserving memoryview objects instead of converting them to bytes unnecessarily. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 3a81ccb commit 96a388e

File tree

1 file changed

+32
-12
lines changed

1 file changed

+32
-12
lines changed

src/lmstudio/history.py

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
Sequence,
1919
Tuple,
2020
TypeAlias,
21+
Union,
2122
cast,
2223
get_args as get_typeform_args,
2324
runtime_checkable,
@@ -27,6 +28,8 @@
2728
Self,
2829
# Native in 3.13+
2930
TypeIs,
31+
# Native in Python 3.12+
32+
Buffer
3033
)
3134

3235
from msgspec import to_builtins
@@ -529,31 +532,47 @@ def add_tool_result(self, result: ToolCallResultInput) -> ToolResultMessage:
529532
return message
530533

531534

532-
LocalFileInput = BinaryIO | bytes | str | os.PathLike[str]
535+
LocalFileInput = BinaryIO | Buffer | str | os.PathLike[str]
533536

534537

535538
# Private until file handle caching support is part of the published SDK API
536539

537540

538-
def _get_file_details(src: LocalFileInput) -> Tuple[str, bytes]:
541+
def _get_file_details(src: LocalFileInput) -> Tuple[str, Buffer]:
539542
"""Read file contents as binary data and generate a suitable default name."""
540-
if isinstance(src, bytes):
541-
# We process bytes as raw data, not a bytes filesystem path
542-
data = src
543-
name = str(uuid.uuid4())
544-
elif hasattr(src, "read"):
543+
# Try to handle buffer protocol objects first (unless it's a path)
544+
if not isinstance(src, (str, os.PathLike)) and not hasattr(src, "read"):
545545
try:
546-
data = src.read()
546+
# If already a memoryview, just use it directly
547+
if isinstance(src, memoryview):
548+
data: Buffer = src
549+
else:
550+
# Try to create a memoryview - this will work for any buffer protocol object
551+
# including bytes, bytearray, array.array, numpy arrays, etc.
552+
data = memoryview(src)
553+
name = str(uuid.uuid4())
554+
return name, data
555+
except TypeError:
556+
# Not a buffer protocol object, fall through to other checks
557+
pass
558+
559+
if hasattr(src, "read"):
560+
try:
561+
data: Buffer = src.read()
547562
except OSError as exc:
548563
# Note: OSError details remain available via raised_exc.__context__
549564
err_msg = f"Error while reading {src!r} ({exc!r})"
550565
raise LMStudioOSError(err_msg) from None
551566
name = getattr(src, "name", str(uuid.uuid4()))
567+
# data is bytes here, which is a Buffer type
568+
return name, data
552569
else:
570+
# At this point, src must be a path-like object
571+
src_path_input = cast(Union[str, os.PathLike[str]], src)
553572
try:
554-
src_path = Path(src)
573+
src_path = Path(src_path_input)
555574
except Exception as exc:
556-
err_msg = f"Expected file-like object, filesystem path, or bytes ({exc!r})"
575+
err_msg = f"Expected file-like object, filesystem path, or buffer ({exc!r})"
557576
raise LMStudioValueError(err_msg) from None
558577
try:
559578
data = src_path.read_bytes()
@@ -562,7 +581,8 @@ def _get_file_details(src: LocalFileInput) -> Tuple[str, bytes]:
562581
err_msg = f"Error while reading {str(src_path)!r} ({exc!r})"
563582
raise LMStudioOSError(err_msg) from None
564583
name = str(src_path.name)
565-
return name, data
584+
# data is bytes here, which is a Buffer type
585+
return name, data
566586

567587

568588
_ContentHash: TypeAlias = bytes
@@ -573,7 +593,7 @@ class _LocalFileData:
573593
"""Local file data to be added to a chat history."""
574594

575595
name: str
576-
raw_data: bytes
596+
raw_data: Buffer
577597

578598
def __init__(self, src: LocalFileInput, name: str | None = None) -> None:
579599
default_name, raw_data = _get_file_details(src)

0 commit comments

Comments
 (0)