fix(huggingface): pass prompt as tuple to Thread args instead of a set#13677
fix(huggingface): pass prompt as tuple to Thread args instead of a set#13677frankgoldfish wants to merge 1 commit intomicrosoft:mainfrom
Conversation
In `HuggingFaceTextCompletion._inner_get_streaming_text_contents`, the
`Thread` constructor was called with `args={prompt}`, which creates a Python
**set** `{prompt}` rather than the required positional-arguments **tuple**
`(prompt,)`.
`threading.Thread` requires `args` to be a sequence (tuple or list) that is
unpacked as positional arguments to the `target` callable. Passing a set
causes undefined iteration order and, for a single-element set containing a
string, the string would be iterated character by character instead of being
passed as a single argument, leading to incorrect or failing invocations of
`self.generator`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes the HuggingFace streaming text completion thread invocation by passing the prompt as a one item tuple to Thread(args=...), matching threading.Thread expectations and preventing incorrect positional argument handling.
Changes:
- Replace
args={prompt}(set literal) withargs=(prompt,)(tuple) when constructing the streaming generation thread.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # See https://github.com/huggingface/transformers/blob/main/src/transformers/generation/streamers.py#L159 | ||
| thread = Thread( | ||
| target=self.generator, args={prompt}, kwargs=settings.prepare_settings_dict(streamer=streamer) | ||
| target=self.generator, args=(prompt,), kwargs=settings.prepare_settings_dict(streamer=streamer) |
There was a problem hiding this comment.
PR description states that passing args={prompt} results in the target being called as generator({prompt}), but Thread ultimately calls self._target(*self._args, ...) and *-unpacking a set passes its elements, not the set itself (for a single string element it becomes generator(prompt)). Consider updating the PR description to reflect the actual failure mode (wrong type and nondeterministic ordering, and potential issues if more args are ever added) so the rationale is technically accurate.
| thread = Thread( | ||
| target=self.generator, args={prompt}, kwargs=settings.prepare_settings_dict(streamer=streamer) | ||
| target=self.generator, args=(prompt,), kwargs=settings.prepare_settings_dict(streamer=streamer) | ||
| ) |
There was a problem hiding this comment.
Current unit tests for HuggingFaceTextCompletion streaming patch Thread but do not assert that it is constructed with args=(prompt,). Since this change fixes a subtle calling bug, please add an assertion in the existing streaming unit test to verify the exact Thread(..., args=..., kwargs=...) call so the regression is caught automatically.
Summary
In
HuggingFaceTextCompletion._inner_get_streaming_text_contents, thethreading.Threadconstructor was called withargs={prompt}(a set literal) instead ofargs=(prompt,)(a tuple).Bug
threading.Threadrequiresargsto be a sequence (tuple or list) that is unpacked as positional arguments to thetargetcallable. When asetis passed:Threadimplementation internally callsself._target(*self._args, **self._kwargs). When_argsis a set containing a string like"hello world", Python unpacks the set — but sinceThreadstores it and passes it directly to the target function with*args, passing a set actually works differently than a tuple. Thegenerator(a HuggingFacepipeline) expects the prompt as its first positional argument. A set literal{prompt}evaluates to the set{prompt}, and when passed via*argsunpacking isgenerator({prompt})— the generator receives the set object as its first argument rather than the string.Fix
Files Changed
python/semantic_kernel/connectors/ai/hugging_face/services/hf_text_completion.pyTest plan
HuggingFaceTextCompletionstreaming works correctly by passing the prompt string as the first positional argument to the pipeline🤖 Generated with Claude Code