diff --git a/NEWS.md b/NEWS.md index 3da3e51f..d6190938 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # ellmer (development version) +* `chat_claude()` and `chat_bedrock()` no longer choke after receiving an + output that consists only of whitespace (#376). + * `live_browser()` now initializes `shinychat::chat_ui()` with the messages from the chat turns, rather than replaying the turns server-side (#381). diff --git a/R/provider-bedrock.R b/R/provider-bedrock.R index 58466cd1..44854997 100644 --- a/R/provider-bedrock.R +++ b/R/provider-bedrock.R @@ -284,7 +284,11 @@ method(as_json, list(ProviderBedrock, Turn)) <- function(provider, x) { } method(as_json, list(ProviderBedrock, ContentText)) <- function(provider, x) { - list(text = x@text) + if (is_whitespace(x@text)) { + list(text = "[empty string]") + } else { + list(text = x@text) + } } method(as_json, list(ProviderBedrock, ContentImageRemote)) <- function( diff --git a/R/provider-claude.R b/R/provider-claude.R index 06dbda88..94257dbf 100644 --- a/R/provider-claude.R +++ b/R/provider-claude.R @@ -266,7 +266,11 @@ method(as_json, list(ProviderClaude, Turn)) <- function(provider, x) { } method(as_json, list(ProviderClaude, ContentText)) <- function(provider, x) { - list(type = "text", text = x@text) + if (is_whitespace(x@text)) { + list(type = "text", text = "[empty string]") + } else { + list(type = "text", text = x@text) + } } method(as_json, list(ProviderClaude, ContentPDF)) <- function(provider, x) { diff --git a/R/utils.R b/R/utils.R index 2e8e608b..99fdd8ce 100644 --- a/R/utils.R +++ b/R/utils.R @@ -128,3 +128,7 @@ modify_list <- function(x, y) { utils::modifyList(x, y) } + +is_whitespace <- function(x) { + grepl("^(\\s|\n)*$", x) +} diff --git a/tests/testthat/test-provider-bedrock.R b/tests/testthat/test-provider-bedrock.R index c2300f00..6ca5df48 100644 --- a/tests/testthat/test-provider-bedrock.R +++ b/tests/testthat/test-provider-bedrock.R @@ -72,6 +72,13 @@ test_that("can use pdfs", { test_pdf_local(chat_fun) }) +# Provider idiosynchronies ----------------------------------------------- + +test_that("continues to work after whitespace only outputs (#376)", { + chat <- chat_bedrock() + chat$chat("Respond with only two blank lines") + expect_equal(chat$chat("What's 1+1? Just give me the number"), "2") +}) # Auth -------------------------------------------------------------------- diff --git a/tests/testthat/test-provider-claude.R b/tests/testthat/test-provider-claude.R index edcabcba..1171c496 100644 --- a/tests/testthat/test-provider-claude.R +++ b/tests/testthat/test-provider-claude.R @@ -62,3 +62,9 @@ test_that("can set beta headers", { req <- chat_request(provider) expect_equal(req$headers$`anthropic-beta`, c("a", "b")) }) + +test_that("continues to work after whitespace only outputs (#376)", { + chat <- chat_claude() + chat$chat("Respond with only two blank lines") + expect_equal(chat$chat("What's 1+1? Just give me the number"), "2") +}) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index 79c5cc73..2f902915 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -10,3 +10,11 @@ test_that("informative error if no key", { expect_false(key_exists("FOO")) expect_snapshot(key_get("FOO"), error = TRUE) }) + +test_that("detects whitespace", { + expect_true(is_whitespace("\n\n\n \t")) + expect_true(is_whitespace("")) + + expect_false(is_whitespace("a")) + expect_false(is_whitespace(".")) +})