Skip to content

fix: strip OpenAI 'annotations' field from outbound Databricks chat messages - #110

Merged
josemaria-vilaplana merged 3 commits into
upstream-sync/v1.83.14-stablefrom
fix/databricks-strip-annotations-from-assistant-content
Jun 5, 2026
Merged

fix: strip OpenAI 'annotations' field from outbound Databricks chat messages#110
josemaria-vilaplana merged 3 commits into
upstream-sync/v1.83.14-stablefrom
fix/databricks-strip-annotations-from-assistant-content

Conversation

@josemaria-vilaplana

Copy link
Copy Markdown

Summary

Companion to #109. #109 fixed the streaming-side corruption that broke turn-1 tool calls; this one fixes the request-side corruption that breaks turn 2+ of any conversation.

OpenAI added an annotations field to chat-completion content blocks for citation parity with the Responses API. LiteLLM normalizes every provider response to that spec, so even when the upstream is Anthropic-via-Databricks (e.g. databricks-claude-sonnet-4-6) the chat-completion response surfaced to the caller may include annotations on text content blocks.

The OpenAI Agents SDK persists this OpenAI-shaped history verbatim and replays it on every follow-up turn. Databricks Model Serving rejects the unknown field on input with:

400 BAD_REQUEST - messages.<n>.content.<m>.text.annotations:
Extra inputs are not permitted

Symptom in CARTO: turn 1 of a UI chat against databricks-claude-* succeeds; turn 2 always fails with the above.

Changes

  • litellm/llms/databricks/chat/transformation.py — new _strip_openai_annotations() helper next to _sanitize_empty_content, called from _transform_messages so every outbound message has annotations stripped from its content blocks.
  • tests/test_litellm/llms/databricks/chat/test_databricks_chat_transformation.py — four regression tests:
    • Helper unit: strips annotations, ignores string content, no-op when absent.
    • Transformer integration: assistant message with annotations survives _transform_messages cleanly.

Test plan

  • New tests pass with the fix; bisect-verified the transformer test fails without the call site.
  • Full test_databricks_chat_transformation.py suite: 16/16 pass.
  • Validate end-to-end against a CARTO devkit / ded29 with a non-GPT Databricks model — exercise a multi-turn chat (turn 2 was the failure mode).

Notes

…essages

OpenAI added an 'annotations' field to chat-completion content blocks for
citation parity with the Responses API. LiteLLM normalizes every provider
response to that spec, so even when the upstream is Anthropic-via-Databricks
the chat-completion response surfaced to the caller may include annotations
on text content blocks.

The OpenAI Agents SDK persists this OpenAI-shaped history verbatim and
replays it on every follow-up turn. Databricks Model Serving rejects the
unknown field on input with:

  400 BAD_REQUEST - messages.<n>.content.<m>.text.annotations:
  Extra inputs are not permitted

Strip annotations from each content block in Databricks's outbound
_transform_messages, next to _sanitize_empty_content. Adds a helper and
four regression tests (unit + transformer).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@josemaria-vilaplana
josemaria-vilaplana marked this pull request as ready for review June 3, 2026 10:11
josemaria-vilaplana and others added 2 commits June 4, 2026 11:31
Companion to the chunk_parser deletion in #109. Databricks's streaming
protocol emits parameterless tool_call arguments as one or two
empty-string deltas (no "{}"), which accumulate to "" in the consumer.
The OpenAI Agents SDK persists that empty string in conversation history
and replays it on every follow-up turn. Databricks Model Serving rejects
the unknown shape with:

    INVALID_PARAMETER_VALUE: Param 'arguments' in the tool_calls
    function specification is not a valid JSON string. No content to
    map due to end-of-input

Validated empirically by calling Databricks Sonnet 4-6 directly: the
non-streaming response returns arguments='{}' but the streamed response
emits two delta chunks both with arguments=''. #109's deletion only
prevented LiteLLM from corrupting a valid '{}' to ''; it did not produce
'{}' when the model natively streamed ''.

Coerce empty/missing tool_call arguments to '{}' in the outbound
_transform_messages so subsequent turns send valid JSON, alongside the
existing _sanitize_empty_content and _strip_openai_annotations helpers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…chunk

Pair to the outbound _normalize_empty_tool_call_arguments fix. The
outbound coercion in _transform_messages prevents Databricks from
rejecting a replay with INVALID_PARAMETER_VALUE, but the SAME-TURN
consumer (OpenAI Agents SDK runtime, frontend tool runners) still calls
JSON.parse on whatever the stream yielded — empty string from
Databricks's parameterless arguments protocol — and surfaces:

    An error occurred while parsing tool arguments.

Default arguments to "{}" on the chunk that introduces the tool_call
(i.e. the chunk carrying function.name) so the consumer's first observed
value is valid JSON. Subsequent args-only delta chunks stay empty so
their concatenation doesn't double up ("{}" + "" + "" = "{}").

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@josemaria-vilaplana
josemaria-vilaplana merged commit de7b5de into upstream-sync/v1.83.14-stable Jun 5, 2026
5 checks passed
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