Skip to content

Drop empty ContentText in OpenAI-compatible serialization#935

Open
JamesHWade wants to merge 2 commits intotidyverse:mainfrom
JamesHWade:fix/databricks-empty-content-text
Open

Drop empty ContentText in OpenAI-compatible serialization#935
JamesHWade wants to merge 2 commits intotidyverse:mainfrom
JamesHWade:fix/databricks-empty-content-text

Conversation

@JamesHWade
Copy link
Contributor

Summary

  • Fixes chat_databricks() HTTP 400 errors when conversation history contains empty ContentText("") objects, which occur during tool calling when Databricks returns content: "" instead of content: null
  • Filters out empty ContentText during as_json() serialization for ProviderOpenAICompatible, following the existing pattern from the Claude provider
  • Drops entirely empty assistant turns (after filtering) to avoid API errors

Closes #932

Test plan

  • Added unit tests verifying empty ContentText is dropped during serialization
  • Added test for assistant turns with only empty ContentText (dropped entirely)
  • Added test for mixed content (empty text stripped, real content preserved)
  • Added test for empty text with tool requests (text stripped, tools preserved)
  • Existing test suite passes

🤖 Generated with Claude Code

)

Databricks returns `content: ""` for tool-only assistant turns, which
creates `ContentText("")` objects. When replayed, these empty text blocks
cause HTTP 400 errors. Filter out empty ContentText during as_json()
serialization, following the existing pattern from the Claude provider.

Closes tidyverse#932

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
contents <- x@contents[!is_empty_text]
if (length(contents) == 0) {
# Drop empty assistant turns to avoid an API error
return(list())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should probably be a NULL?

))
)

# Empty ContentText is stripped but tool requests are preserved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a separate test I think

content = list(list(type = "text", text = "Hello"))
))
)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're missing a test for the (length(contents) == 0) case? i.e. something like list(ContentText(""), ContentText("")). I'd also like to see a live test for that because my recollection from doing this work on Claude is that will cause a problem.

- In value_turn(), treat content: "" as content: null so ContentText("")
  is never created from provider responses (root cause fix)
- Change return(list()) to return(NULL) for empty assistant turns
- Split tool request test into its own test_that()
- Add test for multiple empty ContentText values
- Add test that value_turn() preserves tool requests when dropping empty text
- Add live Databricks test for continuing chat after empty assistant content

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@JamesHWade
Copy link
Contributor Author

Thanks for the review! Addressed all three points:

  1. Changed return(list()) to return(NULL) for consistency
  2. Split the tool request test into its own test_that()
  3. Added test for the all-empty case (list(ContentText(""), ContentText(""))) and a live Databricks test

Also fixed the root cause: value_turn() now treats content: "" as content: null, so ContentText("") is never created from provider responses in the first place. The stripping in as_json() remains as a safety net for manually constructed turns.

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.

chat_databricks(): HTTP 400 when conversation history contains empty ContentText

2 participants