Python: FunctionCallContent.combine_arguments drops a streamed "{}" chunk, producing invalid JSON arguments
Maintainers usually reply within 4 days
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 82/100
Research direction
Start in python/semantic_kernel/contents/function_call_content.py at FunctionCallContent.combine_arguments(), then inspect the reduction in chat_completion_client_base.py and the OpenAI connector's tool.function.arguments deltas. Reproduce the listed delta sequences and verify that accumulated arguments preserve each "{}" chunk, produce valid JSON, and no longer raise FunctionCallInvalidArgumentsException.
Written by the indexing model from the issue text.
Description
What happens
When a streamed function call has its arguments split across chunks, FunctionCallContent.combine_arguments() silently drops any chunk that is exactly the two characters {}. The accumulated arguments string is then invalid JSON, and parse_arguments() raises FunctionCallInvalidArgumentsException.
Where
python/semantic_kernel/contents/function_call_content.py, FunctionCallContent.combine_arguments():
EMPTY_VALUES: Final[list[str | None]] = ["", "{}", None]
...
if arg1 in EMPTY_VALUES and arg2 in EMPTY_VALUES:
return "{}"
if arg1 in EMPTY_VALUES:
return arg2 or "{}"
if arg2 in EMPTY_VALUES:
return arg1 or "{}"
return (arg1 or "") + (arg2 or "")
The "{}" sentinel is meant for a whole "no arguments" value, for example a first chunk that is just a placeholder. But __add__ is also used to concatenate raw token deltas (chat_completion_client_base.py does reduce(lambda x, y: x + y, all_messages) over the stream, and the OpenAI connector passes tool.function.arguments from each delta straight into FunctionCallContent(arguments=...)). A mid-stream fragment that happens to equal {} is an empty JSON object that belongs in the output, not a placeholder. It gets thrown away.
Repro
Tested with semantic-kernel==1.44.1. I diffed function_call_content.py against current main and the file is identical.
from functools import reduce
from semantic_kernel.contents import StreamingChatMessageContent, FunctionCallContent, AuthorRole
def chunk(i, args):
fc = FunctionCallContent(id="call_1" if i == 0 else None, index=0,
name="search-find" if i == 0 else None, arguments=args)
return StreamingChatMessageContent(role=AuthorRole.ASSISTANT, items=[fc], choice_index=0)
deltas = ['{"query":"x","filters":', '{}', '}']
full = reduce(lambda a, b: a + b, [chunk(i, d) for i, d in enumerate(deltas)])
call = [i for i in full.items if isinstance(i, FunctionCallContent)][0]
print("concatenated deltas:", "".join(deltas))
print("accumulated :", call.arguments)
call.parse_arguments()
Output:
concatenated deltas: {"query":"x","filters":{}}
accumulated : {"query":"x","filters":}
...
semantic_kernel.exceptions.content_exceptions.FunctionCallInvalidArgumentsException: Function Call arguments are not valid JSON even after preprocessing.
Other splits fail the same way:
| deltas | accumulated | expected |
|---|---|---|
'{"a":[', '{}', ',', '{}', ']}' |
{"a":[,]} |
{"a":[{},{}]} |
'{"a": {"b":', '{}', '}}' |
{"a": {"b":}} |
{"a": {"b":{}}} |
Expected
The accumulated string equals the concatenation of the deltas, so the arguments parse to {"query": "x", "filters": {}}.
Actual
The {} delta is dropped, the JSON is broken, and the auto function invocation path fails on parse_arguments() for a call the model produced correctly.
Note on likelihood
This needs a tokenizer or provider to emit {} as its own delta. That is a valid and common token for compact JSON, and some providers chunk arbitrarily, but I did not capture it from a live model. The defect is in the merge logic and reproduces deterministically with the deltas above.
Suggested fix
Only treat "{}" as an empty placeholder when the other side is empty or is itself a complete-looking object. One option: keep "" and None as always-empty, and treat "{}" as empty only when it is the accumulated arg1 and arg2 starts a new object, or when both sides are "{}". Otherwise fall through to plain string concatenation.
I searched issues and PRs for combine_arguments, EMPTY_VALUES, FunctionCallContent.__add__ and streaming function-call argument problems. Nothing covers this.
- Dominant language
- C#
- Stars
- 28.6k
- Forks
- 4.8k
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 19
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from microsoft/semantic-kernel
-
.Net: gpt-image-1 is the default image model in the .NET OpenAI connector, and OpenAI shuts it down on October 23Possibly taken @nightcityblade claimed this 3 days ago. Open.NET triage
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
microsoft/semantic-kernel#14526 · 1 comment ·
Maintainers usually reply within 4 days
-
Python: VolatileMemoryStore.get_batch and get_nearest_matches ignore with_embeddings=False (deepcopy result is discarded)Possibly taken @VANDRANKI claimed this 5 days ago. Openpython triage
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
microsoft/semantic-kernel#14522 ·
Maintainers usually reply within 4 days
-
Python: VolatileMemoryStore.get_nearest_match returns an un-awaited coroutine instead of a (MemoryRecord, score) tuplePossibly taken @VANDRANKI claimed this 5 days ago. Openpython triage
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
microsoft/semantic-kernel#14521 ·
Maintainers usually reply within 4 days
-
.Net: Bug: BinaryContent does not decode the %xx escapes of a non-base64 data URIPossibly taken @Laurianti claimed this 5 days ago. Open.NET triage
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
microsoft/semantic-kernel#14518 ·
Maintainers usually reply within 4 days
-
.Net: Python: KernelJsonSchemaBuilder emits {"type": "object"} with no enum for typing.Literal parameters and fieldsPossibly taken @VANDRANKI claimed this 6 days ago. Open.NET python triage
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
microsoft/semantic-kernel#14511 ·
Maintainers usually reply within 4 days
All issues in microsoft/semantic-kernel
Similar issues
-
S: Untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
space-wizards/space-station-14#46357 ·
Maintainers usually reply within 1 day
-
Versioning_oM: Remove unneeded depeendecy on Test_oMPossibly taken @IsakNaslundBh claimed this today. Opentype:bug type:compliance
Difficulty 2/5 Under an hour Newbie friendliness 85/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
MicrosoftLearning/PL-400_Microsoft-Power-Platform-Developer#231 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
joinrpg/joinrpg-net#5313 ·
Maintainers usually reply within 1 day