-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement batch processing #143
Comments
Do you think this code could live in ellmer itself? Do you have any sense of what the UI might look like? |
I was thinking that in a batch scenario you still want to be able to do all the same things that you do in a regular chat, but you just want to do many of them at once. So maybe an interface like this would work: library(ellmer)
chat <- chat_openai("You're reply succintly")
chat$register_tool(tool(function() Sys.Date(), "Return the current date"))
chat$chat_batch(list(
"What's the date today?",
"What's the date tomorrow?",
"What's the date yesterday?",
"What's the date next week?",
"What's the date last week?",
)) I think the main challenge is what would Other thoughts:
|
After revisiting this, I do think my original ideas were ambitious, and I would like to see this code in the ellmer package if possible. I revised my original post to simplify the concept. I think your interface idea is a good start, and as for the returned object, would it be possible to return a list not only of the text answers, but a nested list with each chat object for diagnostics? Although that might be a beefy object if you have big batches (maybe only return the chat object if there was an issue?). |
@t-emery - Adding Teal because I know he has experience using ellmer for batch processing. |
FWIW, my use-case was batched structured data extraction. I was reading text data from a tibble and doing structured data extraction (x 18,000). In my initial attempts, I found that I was using far more tokens than made sense given the inputs. Eventually I found the workable answer was to clear the turns after each time. Then everything worked as expected. I'm new to using LLM APIs, so I have great humility that I might be missing something simple. I haven't had time to think about this deeply yet, and I haven't figured out what part of this is a documentation issue (creating documentation about best practices for running a lot of data) versus a feature issue. Here are the relevant functions. The key fix was:
# 1. Core Classification Function ----
classify_single_project <- function(text, chat, type_spec) {
result <- purrr::safely(
~ chat$extract_data(text, type = type_spec),
otherwise = NULL,
quiet = TRUE
)()
tibble::tibble(
success = is.null(result$error),
error_message = if(is.null(result$error)) NA_character_ else as.character(result$error),
classification = list(result$result)
)
}
# 2. Process a chunk of data ----
# 3. Modify process_chunk to handle provider types
process_chunk <- function(chunk, chat, type_spec, provider_type = NULL) {
# Use the provider_type parameter to determine which classification function to use
classify_fn <- if(provider_type == "deepseek") {
classify_single_project_deepseek
} else {
classify_single_project
}
chunk |>
mutate(
classification_result = map(
combined_text,
~{
# Clear all prior turns so we start fresh for *each* record
chat$set_turns(list())
classify_fn(.x, chat, type_spec)
}
)
) |>
unnest(classification_result) |>
mutate(
primary_class = map_chr(classification, ~.x$classification$primary %||% NA_character_),
confidence = map_chr(classification, ~.x$classification$confidence %||% NA_character_),
project_type = map_chr(classification, ~.x$classification$project_type %||% NA_character_),
justification = map_chr(classification, ~.x$justification %||% NA_character_),
evidence = map_chr(classification, ~.x$evidence %||% NA_character_)
) |>
select(-classification)
} |
@t-emery a slightly better way to ensure that the chats are standalone is to call |
Beyond retries, it would be great to handle interrupted batches and resume when you re-call the function. For example: library(ellmer)
chat <- chat_openai("You reply succinctly")
chat$register_tool(tool(
function() Sys.Date(),
"Return the current date"
))
prompts <- list(
"What's the date today?",
"What's the date tomorrow?",
"What's the date yesterday?",
"What's the date next week?",
"What's the date last week?"
)
# Initial processing (interrupted and returns a partial object)
responses <- chat$chat_batch(prompts)
# Resume processing
responses <- chat$chat_batch(prompts, responses) Where chat_batch <- function(prompts, last_chat = NULL) {
if (!is.null(last_chat) && !last_chat$complete) {
chat$process_batch(prompts, start_at = last_chat$last_index + 1)
} else {
chat$process_batch(prompts)
}
} |
@dylanpieper I think it's a bit better to return a richer object that lets you resume. Something like this maybe: batched_chat <- chat$chat_batch(prompts)
batched_chat$process()
# ctrl + c
batched_chat$process() # resumes where the work left off That object would also be the way you get either rich chat objects or simple text responses: batched_chat$texts()
batched_chat$chats() For the batch (not parallel) case, where it might take up to 24 hours, the function would also need to serialise something to disk, so if your R process completely dies, you can resume it in a fresh session: batched_chat <- resume(some_path) (All of these names are up for discussion, I just brain-dumped quickly.) |
Implement batch processing into ellmer to process lists of inputs through a single prompt. Batch processing enables running multiple inputs to get multiple responses while maintaining safety and ability to leverage ellmer's other features.
Basic Features (likely in scope):
Ambitious Features / Extensions (likely out of scope):
The text was updated successfully, but these errors were encountered: