Repository navigation
Conversation
…s from the current position _invoke_progress tried progress(uploaded, total) and, on TypeError, fell back to progress(chunk_size). The except also caught TypeErrors raised inside the user's callback, so a two-argument callback that failed on its own (the docstring's own example does when total is None: 100 * uploaded / total) was silently called again with one argument and the user saw 'missing 1 required positional argument: total' instead of their error. Arity now comes from inspect.signature once per call; callables that cannot be inspected get the documented two-argument form. _chunk_file_from_fileobj (sync and async) computed total_bytes as the absolute end offset, so a file object positioned past a header reported a total the upload could never reach. It is now end minus current position, which is what gets uploaded. Tests: one-argument plain function gets chunk sizes; a TypeError from inside the callback propagates with its own message; a BytesIO read past a header reports (payload, payload) on the last call. Both new behaviours fail on main.
_order_to_pb, _sort_by_to_pb, _collection_sort_by_to_pb, _document_sort_by_to_pb and _hnsw_metric_to_pb matched the known literals and returned a default (or the UNKNOWN enum) for anything else, so order="dsc" listed ascending, sort_by="created" sorted by the default column, and metric_space="cos" created a collection with an unknown metric, all without a word. Every other literal parameter in the SDK raises ValueError with the accepted values (reasoning_effort, service_tier, search mode, image format/quality/resolution). Unknown non-None values now raise ValueError the same way; None keeps its meaning as the default. Tests for each helper; they fail on main.
AsyncClient.files.upload accepted an async def on_progress callback, called it from the async chunk generators through the sync _invoke_progress, and dropped the returned coroutine. The callback body never ran and Python emitted 'coroutine ... was never awaited' once per chunk. Writing the callback as a coroutine is the natural thing to do in async code and the type did not forbid it. _invoke_progress now returns the callback's result; the three async generators call _async_invoke_progress, which awaits the result when it is awaitable. Sync callbacks and tqdm-like objects are unchanged. ProgressCallback admits awaitable-returning callables. Test: an async def callback on AsyncClient.files.upload runs on every chunk with RuntimeWarning raised as an error; fails on main.
xai-org#98 (2026-02-17) changed PollTimer's default interval from 100 ms to 1 s but left the six defer / defer_batch / _defer docstrings in sync/chat.py and aio/chat.py saying "defaults to 100 milliseconds". A caller who reads the docstring and skips interval= polls ten times less often than documented. tests/poll_timer_test.py pins the PollTimer defaults and checks that the six docstrings state the value PollTimer actually uses, so the next change to the default fails a test instead of drifting again.
image(url, detail=...) mapped "low" and "high" and let anything else fall through to DETAIL_AUTO, so image(url, detail="hgh") sent the image at auto resolution with no signal to the caller. Every other literal in the chat API (reasoning_effort, tool_choice, response_format, include, agent_count, service_tier, search mode) and the image and video converters raise ValueError on a value outside the Literal. _image_detail_to_proto matches the three literals plus None, which keeps meaning auto, and raises the same shaped ValueError as the rest. Four new cases fail on main; 877 pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
One branch with the five
files,collectionsandchatfixes currently open as separate PRs, cherry-picked in numeric order on top ofmain60188ed, with the five CHANGELOG entries merged by hand into one[Unreleased]section. Each change keeps its own commit, so this can be reviewed as five small diffs or landed as one. If a single PR is easier to review, this replaces #232, #233, #234, #236 and #239; if the separate PRs are preferred, close this one.files.uploadreads the progress callback's arity from its signature once; aTypeErrorraised inside a two-argument callback propagates instead of triggering a second call with the chunk size.totalfor a file object positioned past the start is the bytes left to upload.files.listand thecollectionslisting helpers raiseValueErrorfor an unknownorder,sort_byormetric_spacevalue instead of falling back to the default silently.AsyncClient.files.uploadawaits anasync defprogress callback instead of creating a coroutine that is never awaited.deferdocstrings quote the current 1 s poll interval default instead of the old value.xai_sdk.chat.imageraisesValueErrorfor adetailvalue other than"auto","low","high"orNoneinstead of sending"auto".Not included: #240 (
stream_batchread order), which overlaps with #202 from @latent-9; the two are left side by side for the maintainers to pick. #224, #228 and #231 are separate bundles of other contributors' work and stay as they are.Checks on the combined branch
files.pyandtests/sync/files_test.py(touched by files: decide progress callback arity from its signature; total counts from the current position #232, files, collections: reject unknown order, sort_by and metric values #233 and files: await an async progress callback on the async upload path #234) equalgit merge-treeof the files: await an async progress callback on the async upload path #234 and files, collections: reject unknown order, sort_by and metric values #233 branches (files: await an async progress callback on the async upload path #234 is stacked on files: decide progress callback arity from its signature; total counts from the current position #232).CHANGELOG.mdis the only hand-merged file: two### Changedbullets and two### Fixedbullets, same text as the originals.pytest: 890 passed.ruff checkandruff format --check: clean.pyrightwith the project.venv: 0 errors, 0 warnings.git merge-treeagainstmain: clean. Against every other open PR: clean, except Ship the py.typed marker the Typing :: Typed classifier promises #241, fix(chat): expose stream_batch content for every response #202 and chat: stream_batch responses share the stream buffers #240, which conflict only inCHANGELOG.mdbecause they also add a bullet under[Unreleased].Type of Change
Checklist