Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
`gen_ai.output.messages`, and `gen_ai.system_instructions`. This is opt-in via the
`OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` environment variable (set to `"true"`), since these payloads may contain user data.
* The `input` column of `token_usage()` and `parallel_chat()` no longer includes a hidden 1.25x pricing weight for Anthropic cache-creation tokens; the column now reports raw integer token counts. Cost calculations are unchanged.
* `chat_openai()` now uses the appropriate prices when a non-default service tier (e.g. `"priority"`) is used (@trangdata, #903).
* `chat_aws_bedrock()` now supports reasoning/thinking content. To enable thinking in Anthropic Claude models, see the `api_args` argument in `?chat_aws_bedrock` for an example (#964).
* New `chat_lmstudio()` and `models_lmstudio()` provide support for [LM Studio](https://lmstudio.ai), a local model server with an OpenAI-compatible API (#963).
* Fixed three bugs that caused errors when streaming web search results: Claude's `citations_delta` events were mishandled, `server_tool_use` input wasn't parsed from JSON during streaming, and OpenAI's `web_search_call` failed for non-search action types like `open_page` (#941).
Expand Down
3 changes: 2 additions & 1 deletion R/provider-openai.R
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,8 @@ method(value_turn, ProviderOpenAI) <- function(
})

tokens <- value_tokens(provider, result)
cost <- get_token_cost(provider, tokens, variant = result$service_tier)
variant <- result$service_tier %||% "default"
cost <- get_token_cost(provider, tokens, variant = variant)
AssistantTurn(
contents = contents,
json = result,
Expand Down
2 changes: 2 additions & 0 deletions R/tokens.R
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ has_cost <- function(provider, model) {
}

get_token_cost <- function(provider, tokens, variant = "") {
check_string(variant, .internal = TRUE)

needle <- data.frame(
provider = provider@name,
model = provider@model,
Expand Down
10 changes: 10 additions & 0 deletions tests/testthat/_snaps/tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@
provider model input output cached_input price
1 testprovider test 1 1 1 $0.00

# informative internal error if variant is missing

Code
get_token_cost(provider, tokens(), variant = NULL)
Condition
Error in `get_token_cost()`:
! `variant` must be a single string, not `NULL`.
i This is an internal error that was detected in the ellmer package.
Please report it at <https://github.com/tidyverse/ellmer/issues> with a reprex (<https://tidyverse.org/help/>) and the full backtrace.

# token_usage() shows price if available

Code
Expand Down
14 changes: 14 additions & 0 deletions tests/testthat/test-provider-openai.R
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,20 @@ test_that("can extract dummy response from malformed JSON", {
)
})

test_that("value_turn handles NULL service_tier gracefully", {
provider <- chat_openai_test()$get_provider()

result <- list(
output = list(
list(type = "message", content = list(list(text = "Hello")))
),
usage = NULL,
service_tier = NULL
)

expect_no_error(value_turn(provider, result))
})

test_that("value_turn() handles web_search_call action types", {
provider <- chat_openai_test()$get_provider()

Expand Down
9 changes: 9 additions & 0 deletions tests/testthat/test-tokens.R
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ test_that("can compute price of tokens with a variant", {
)
})

test_that("informative internal error if variant is missing", {
provider <- test_provider("OpenAI", "gpt-4o")
expect_snapshot(
get_token_cost(provider, tokens(), variant = NULL),
error = TRUE
)
})


test_that("price is NA if we don't have the data for it", {
provider <- test_provider("ClosedAI", "gpt-4o")
expect_equal(
Expand Down
Loading