Skip to content

Five open files, collections and chat fixes in one branch (#232, #233, #234, #236, #239) - #244

Open
deanrie wants to merge 5 commits into
xai-org:mainfrom
deanrie:bundle/files-and-chat-fixes
Open

deanrie wants to merge 5 commits into
xai-org:mainfrom
deanrie:bundle/files-and-chat-fixes

Conversation

@deanrie

@deanrie deanrie commented Oct 27, 2026 •

Copy link
Copy Markdown

Description

One branch with the five files, collections and chat fixes currently open as separate PRs, cherry-picked in numeric order on top of main 60188ed, 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.

Commit Original Change
0cc5c08 #232 files.upload reads the progress callback's arity from its signature once; a TypeError raised inside a two-argument callback propagates instead of triggering a second call with the chunk size. total for a file object positioned past the start is the bytes left to upload.
a983a79 #233 files.list and the collections listing helpers raise ValueError for an unknown order, sort_by or metric_space value instead of falling back to the default silently.
bc01103 #234 AsyncClient.files.upload awaits an async def progress callback instead of creating a coroutine that is never awaited.
e724b49 #236 the defer docstrings quote the current 1 s poll interval default instead of the old value.
db0e8f8 #239 xai_sdk.chat.image raises ValueError for a detail value other than "auto", "low", "high" or None instead of sending "auto".

Not included: #240 (stream_batch read 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

Type of Change

  • Bug fix

Checklist

…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.
@deanrie
deanrie requested a review from a team as a code owner October 27, 2026 06:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant