llm encoding_error ai_generated true

json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 1023 (char 1022) in function call arguments stream

ID: llm/function-call-arguments-truncated-in-stream

Also available as: JSON · Markdown · 中文
85%Fix Rate
90%Confidence
1Evidence
2024-01-10First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
openai-python>=1.0.0 active
gpt-4-0613 active
gpt-3.5-turbo-0613 active

Root Cause

When streaming function calls, the arguments are sent as a JSON string that may be split across multiple chunks, causing incomplete JSON when parsed prematurely.

generic

中文

当流式传输函数调用时,参数作为 JSON 字符串发送,可能被分割到多个块中,导致过早解析时 JSON 不完整。

Official Documentation

https://platform.openai.com/docs/guides/function-calling/streaming-function-calls

Workarounds

  1. 85% success Buffer all function call arguments chunks until a complete JSON can be parsed. Example: `buffer = ""; for chunk in response: if chunk.choices[0].delta.function_call.arguments: buffer += chunk.choices[0].delta.function_call.arguments; try: args = json.loads(buffer); break; except JSONDecodeError: continue`
    Buffer all function call arguments chunks until a complete JSON can be parsed. Example: `buffer = ""; for chunk in response: if chunk.choices[0].delta.function_call.arguments: buffer += chunk.choices[0].delta.function_call.arguments; try: args = json.loads(buffer); break; except JSONDecodeError: continue`
  2. 90% success Use the OpenAI library's built-in function call handling which automatically accumulates arguments: `tool_calls = chunk.choices[0].delta.tool_calls` and use `accumulated_arguments[tool_call_index] += chunk.arguments`.
    Use the OpenAI library's built-in function call handling which automatically accumulates arguments: `tool_calls = chunk.choices[0].delta.tool_calls` and use `accumulated_arguments[tool_call_index] += chunk.arguments`.
  3. 70% success Set `stream_options={"include_usage": True}` to get a final chunk with complete function call info, though this may not always include full arguments.
    Set `stream_options={"include_usage": True}` to get a final chunk with complete function call info, though this may not always include full arguments.

中文步骤

  1. Buffer all function call arguments chunks until a complete JSON can be parsed. Example: `buffer = ""; for chunk in response: if chunk.choices[0].delta.function_call.arguments: buffer += chunk.choices[0].delta.function_call.arguments; try: args = json.loads(buffer); break; except JSONDecodeError: continue`
  2. Use the OpenAI library's built-in function call handling which automatically accumulates arguments: `tool_calls = chunk.choices[0].delta.tool_calls` and use `accumulated_arguments[tool_call_index] += chunk.arguments`.
  3. Set `stream_options={"include_usage": True}` to get a final chunk with complete function call info, though this may not always include full arguments.

Dead Ends

Common approaches that don't work:

  1. 80% fail

    Max_tokens affects the total output length, not the chunking behavior; stream chunks are inherently arbitrary.

  2. 40% fail

    This works but defeats the purpose of streaming for user experience; also, it may not be feasible for long-running calls.

  3. 60% fail

    This is actually a valid approach but requires careful buffering; the dead end is when developers try to parse each chunk individually.