From 41d4eaaf1fecac88286f1e7763c8b71fefc813bc Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 08:43:15 -0500 Subject: [PATCH 01/15] Simplifies vec_prompt functions and renames it --- R/llm-classify.R | 2 +- R/llm-extract.R | 2 +- R/llm-sentiment.R | 2 +- R/llm-summarize.R | 2 +- R/llm-translate.R | 2 +- R/m-backend-prompt.R | 13 ++++--------- 6 files changed, 9 insertions(+), 14 deletions(-) diff --git a/R/llm-classify.R b/R/llm-classify.R index 75e95f9..adf65e5 100644 --- a/R/llm-classify.R +++ b/R/llm-classify.R @@ -44,7 +44,7 @@ llm_classify.data.frame <- function(.data, llm_vec_classify <- function(x, labels, additional_prompt = "") { - llm_vec_prompt( + l_vec_prompt( x = x, prompt_label = "classify", additional_prompt = additional_prompt, diff --git a/R/llm-extract.R b/R/llm-extract.R index e467a5b..1dc25ea 100644 --- a/R/llm-extract.R +++ b/R/llm-extract.R @@ -76,7 +76,7 @@ llm_extract.data.frame <- function(.data, llm_vec_extract <- function(x, labels = c(), additional_prompt = "") { - resp <- llm_vec_prompt( + resp <- l_vec_prompt( x = x, prompt_label = "extract", labels = labels, diff --git a/R/llm-sentiment.R b/R/llm-sentiment.R index 469229a..38a5048 100644 --- a/R/llm-sentiment.R +++ b/R/llm-sentiment.R @@ -53,7 +53,7 @@ globalVariables("ai_analyze_sentiment") llm_vec_sentiment <- function(x, options = c("positive", "negative", "neutral"), additional_prompt = "") { - llm_vec_prompt( + l_vec_prompt( x = x, prompt_label = "sentiment", additional_prompt = additional_prompt, diff --git a/R/llm-summarize.R b/R/llm-summarize.R index 2a95aae..73631e0 100644 --- a/R/llm-summarize.R +++ b/R/llm-summarize.R @@ -52,7 +52,7 @@ globalVariables("ai_summarize") llm_vec_summarize <- function(x, max_words = 10, additional_prompt = "") { - llm_vec_prompt( + l_vec_prompt( x = x, prompt_label = "summarize", additional_prompt = additional_prompt, diff --git a/R/llm-translate.R b/R/llm-translate.R index 9a51703..9266a14 100644 --- a/R/llm-translate.R +++ b/R/llm-translate.R @@ -39,7 +39,7 @@ llm_vec_translate <- function( x, language, additional_prompt = "") { - llm_vec_prompt( + l_vec_prompt( x = x, prompt_label = "translate", additional_prompt = additional_prompt, diff --git a/R/m-backend-prompt.R b/R/m-backend-prompt.R index d34c34b..bf4056c 100644 --- a/R/m-backend-prompt.R +++ b/R/m-backend-prompt.R @@ -93,19 +93,14 @@ m_backend_prompt.mall_defaults <- function(backend, additional = "") { ) } -get_prompt <- function(label, ..., .additional = "") { - defaults <- m_backend_prompt(defaults_get(), additional = .additional) - fn <- defaults[[label]] - fn(...) -} - - -llm_vec_prompt <- function(x, +l_vec_prompt <- function(x, prompt_label = "", additional_prompt = "", valid_resps = NULL, ...) { llm_use(.silent = TRUE, force = FALSE) - prompt <- get_prompt(prompt_label, ..., .additional = additional_prompt) + defaults <- m_backend_prompt(defaults_get(), additional = additional_prompt) + fn <- defaults[[prompt_label]] + prompt <- fn(...) llm_vec_custom(x, prompt, valid_resps = valid_resps) } From 3e8ab72924efb0bd94f1866a207c087f2d9d4895 Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 09:00:55 -0500 Subject: [PATCH 02/15] Moves code from custom to l_vec_prompt, and adds comments --- R/llm-custom.R | 29 ++++------------------ R/m-backend-prompt.R | 57 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/R/llm-custom.R b/R/llm-custom.R index c5d627b..cd670c3 100644 --- a/R/llm-custom.R +++ b/R/llm-custom.R @@ -40,28 +40,9 @@ llm_custom.data.frame <- function(.data, #' @rdname llm_custom #' @export llm_vec_custom <- function(x, prompt = "", valid_resps = NULL) { - llm_use(.silent = TRUE, force = FALSE) - if (!inherits(prompt, "list")) { - p_split <- strsplit(prompt, "\\{\\{x\\}\\}")[[1]] - if (length(p_split) == 1 && p_split == prompt) { - content <- glue("{prompt}\n{{x}}") - } else { - content <- prompt - } - prompt <- list(list(role = "user", content = content)) - } - resp <- m_backend_submit(defaults_get(), x, prompt) - if (!is.null(valid_resps)) { - errors <- !resp %in% valid_resps - resp[errors] <- NA - if (any(errors)) { - cli_alert_warning( - c( - "There were {sum(errors)} predictions with ", - "invalid output, they were coerced to NA" - ) - ) - } - } - resp + l_vec_prompt( + x = x, + prompt = prompt, + valid_resps = valid_resps + ) } diff --git a/R/m-backend-prompt.R b/R/m-backend-prompt.R index bf4056c..4f5f80b 100644 --- a/R/m-backend-prompt.R +++ b/R/m-backend-prompt.R @@ -94,13 +94,54 @@ m_backend_prompt.mall_defaults <- function(backend, additional = "") { } l_vec_prompt <- function(x, - prompt_label = "", - additional_prompt = "", - valid_resps = NULL, - ...) { + prompt_label = "", + additional_prompt = "", + valid_resps = NULL, + prompt = NULL, + ...) { + # Initializes session LLM llm_use(.silent = TRUE, force = FALSE) - defaults <- m_backend_prompt(defaults_get(), additional = additional_prompt) - fn <- defaults[[prompt_label]] - prompt <- fn(...) - llm_vec_custom(x, prompt, valid_resps = valid_resps) + # If there is no 'prompt', then assumes that we're looking for a + # prompt label (sentiment, classify, etc) to set 'prompt' + if (is.null(prompt)) { + defaults <- m_backend_prompt( + backend = defaults_get(), + additional = additional_prompt + ) + fn <- defaults[[prompt_label]] + prompt <- fn(...) + } + # If the prompt is a character, it will convert it to + # a list so it can be processed + if (!inherits(prompt, "list")) { + p_split <- strsplit(prompt, "\\{\\{x\\}\\}")[[1]] + if (length(p_split) == 1 && p_split == prompt) { + content <- glue("{prompt}\n{{x}}") + } else { + content <- prompt + } + prompt <- list( + list(role = "user", content = content) + ) + } + # Submits final prompt to the LLM + resp <- m_backend_submit( + backend = defaults_get(), + x = x, + prompt = prompt + ) + # Checks for invalid output and marks them as NA + if (!is.null(valid_resps)) { + errors <- !resp %in% valid_resps + resp[errors] <- NA + if (any(errors)) { + cli_alert_warning( + c( + "There were {sum(errors)} predictions with ", + "invalid output, they were coerced to NA" + ) + ) + } + } + resp } From 0daa50f1c5e1f722efe0f64d85a92cc395e3f6d8 Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 09:36:36 -0500 Subject: [PATCH 03/15] Adds sentiment tests --- R/m-backend-prompt.R | 6 +++--- R/m-backend-submit.R | 6 +++--- tests/testthat/test-llm-sentiment.R | 22 ++++++++++++++++++++++ 3 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 tests/testthat/test-llm-sentiment.R diff --git a/R/m-backend-prompt.R b/R/m-backend-prompt.R index 4f5f80b..1eb642e 100644 --- a/R/m-backend-prompt.R +++ b/R/m-backend-prompt.R @@ -100,12 +100,12 @@ l_vec_prompt <- function(x, prompt = NULL, ...) { # Initializes session LLM - llm_use(.silent = TRUE, force = FALSE) + backend <- llm_use(.silent = TRUE, force = FALSE) # If there is no 'prompt', then assumes that we're looking for a # prompt label (sentiment, classify, etc) to set 'prompt' if (is.null(prompt)) { defaults <- m_backend_prompt( - backend = defaults_get(), + backend = backend, additional = additional_prompt ) fn <- defaults[[prompt_label]] @@ -126,7 +126,7 @@ l_vec_prompt <- function(x, } # Submits final prompt to the LLM resp <- m_backend_submit( - backend = defaults_get(), + backend = backend, x = x, prompt = prompt ) diff --git a/R/m-backend-submit.R b/R/m-backend-submit.R index 944e462..cb1db60 100644 --- a/R/m-backend-submit.R +++ b/R/m-backend-submit.R @@ -32,13 +32,13 @@ m_backend_submit.mall_ollama <- function(backend, x, prompt) { } #' @export -m_backend_submit.mall_simulate_llm <- function(backend, x, base_prompt) { +m_backend_submit.mall_simulate_llm <- function(backend, x, prompt) { args <- backend class(args) <- "list" if (args$model == "pipe") { - out <- trimws(strsplit(x, "\\|")[[1]][[2]]) + out <- map_chr(x, \(x) trimws(strsplit(x, "\\|")[[1]][[2]])) } else if (args$model == "prompt") { - out <- glue("{base_prompt}\n{x}") + out <- glue("{prompt}\n{x}") } else if (args$model == "echo") { out <- x } else { diff --git a/tests/testthat/test-llm-sentiment.R b/tests/testthat/test-llm-sentiment.R new file mode 100644 index 0000000..ae177f9 --- /dev/null +++ b/tests/testthat/test-llm-sentiment.R @@ -0,0 +1,22 @@ +test_that("Sentiment works", { + test_text <- "this is a test" + llm_use("simulate_llm", "pipe", .silent = TRUE) + expect_equal( + llm_vec_sentiment("this is a test|positive"), + "positive" + ) + expect_message( + x <- llm_vec_sentiment("this is a test|notvalid") + ) + expect_equal(x, as.character(NA)) + + entries <- c("a|positive", "b|negative") + expect_equal( + llm_sentiment(data.frame(x = entries), x), + data.frame(x = entries, .sentiment = c("positive", "negative")) + ) + expect_equal( + llm_sentiment(data.frame(x = entries), x, pred_name = "new"), + data.frame(x = entries, new = c("positive", "negative")) + ) +}) From 65ebaae2cfcc838f7892aad6455ecc80151a5272 Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 10:10:32 -0500 Subject: [PATCH 04/15] Adds tests for extract --- R/llm-extract.R | 3 ++- tests/testthat/test-llm-extract.R | 40 +++++++++++++++++++++++++++++ tests/testthat/test-llm-sentiment.R | 1 - 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 tests/testthat/test-llm-extract.R diff --git a/R/llm-extract.R b/R/llm-extract.R index 1dc25ea..bca7044 100644 --- a/R/llm-extract.R +++ b/R/llm-extract.R @@ -31,8 +31,9 @@ llm_extract.data.frame <- function(.data, additional_prompt = "", pred_name = ".extract") { if (expand_cols && length(labels) > 1) { + text <- dplyr::pull(.data, {{ col }}) resp <- llm_vec_extract( - x = .data$col, + x = text, labels = labels, additional_prompt = additional_prompt ) diff --git a/tests/testthat/test-llm-extract.R b/tests/testthat/test-llm-extract.R new file mode 100644 index 0000000..1c314b5 --- /dev/null +++ b/tests/testthat/test-llm-extract.R @@ -0,0 +1,40 @@ +test_that("Extract works", { + llm_use("simulate_llm", "echo", .silent = TRUE) + + expect_equal( + llm_vec_extract("{\"product\":\"toaster\"}", labels = "product"), + "toaster" + ) + + entries2 <- "{\"product\":\"toaster\", \"product\":\"TV\"}" + entries2_result <- "toaster|TV" + expect_equal( + llm_vec_extract( + entries2, + labels = "product" + ), + entries2_result + ) + expect_equal( + llm_extract(data.frame(x = entries2), x, labels = "product"), + data.frame(x = entries2, .extract = entries2_result) + ) + expect_equal( + llm_extract( + .data = data.frame(x = entries2), + col = x, + labels = c("product1", "product2"), + expand_cols = TRUE + ), + data.frame(x = entries2, product1 = "toaster", product2 = "TV") + ) + expect_equal( + llm_extract( + .data = data.frame(x = entries2), + col = x, + labels = c(y = "product1", z = "product2"), + expand_cols = TRUE + ), + data.frame(x = entries2, y = "toaster", z = "TV") + ) +}) \ No newline at end of file diff --git a/tests/testthat/test-llm-sentiment.R b/tests/testthat/test-llm-sentiment.R index ae177f9..d9b0cb6 100644 --- a/tests/testthat/test-llm-sentiment.R +++ b/tests/testthat/test-llm-sentiment.R @@ -1,5 +1,4 @@ test_that("Sentiment works", { - test_text <- "this is a test" llm_use("simulate_llm", "pipe", .silent = TRUE) expect_equal( llm_vec_sentiment("this is a test|positive"), From d2466165c97a21ced3e213239eca1ab5689a1eb9 Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 10:15:22 -0500 Subject: [PATCH 05/15] Adds summarize tests --- tests/testthat/test-llm-summarize.R | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/testthat/test-llm-summarize.R diff --git a/tests/testthat/test-llm-summarize.R b/tests/testthat/test-llm-summarize.R new file mode 100644 index 0000000..2ad1966 --- /dev/null +++ b/tests/testthat/test-llm-summarize.R @@ -0,0 +1,22 @@ +test_that("Summarize works", { + test_text <- "this is a test" + llm_use("simulate_llm", "echo", .silent = TRUE) + expect_equal( + llm_vec_summarize(test_text), + test_text + ) + + expect_equal( + llm_summarize(data.frame(x = test_text), x), + data.frame(x = test_text, .summary = test_text) + ) + + expect_equal( + llm_summarize( + data.frame(x = test_text), + x, + pred_name = "new" + ), + data.frame(x = test_text, new = test_text) + ) +}) From 16543cae6aa2e5596d338a903a6c1e1d09ec2bf1 Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 10:19:21 -0500 Subject: [PATCH 06/15] Adds translate --- tests/testthat/test-llm-translate.R | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/testthat/test-llm-translate.R diff --git a/tests/testthat/test-llm-translate.R b/tests/testthat/test-llm-translate.R new file mode 100644 index 0000000..2a589ec --- /dev/null +++ b/tests/testthat/test-llm-translate.R @@ -0,0 +1,23 @@ +test_that("Translate works", { + test_text <- "this is a test" + llm_use("simulate_llm", "echo", .silent = TRUE) + expect_equal( + llm_vec_translate(test_text, language = "other"), + test_text + ) + + expect_equal( + llm_translate(data.frame(x = test_text), x, language = "other"), + data.frame(x = test_text, .translation = test_text) + ) + + expect_equal( + llm_translate( + data.frame(x = test_text), + x, + pred_name = "new", + language = "other" + ), + data.frame(x = test_text, new = test_text) + ) +}) From 55355027a378dfb88cdcb75296338d5f7d2672e5 Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 10:19:53 -0500 Subject: [PATCH 07/15] styler updates --- R/llm-custom.R | 2 +- R/m-backend-submit.R | 2 +- tests/testthat/test-llm-extract.R | 16 ++++++++-------- tests/testthat/test-llm-sentiment.R | 2 +- tests/testthat/test-llm-translate.R | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/R/llm-custom.R b/R/llm-custom.R index cd670c3..7ceff10 100644 --- a/R/llm-custom.R +++ b/R/llm-custom.R @@ -42,7 +42,7 @@ llm_custom.data.frame <- function(.data, llm_vec_custom <- function(x, prompt = "", valid_resps = NULL) { l_vec_prompt( x = x, - prompt = prompt, + prompt = prompt, valid_resps = valid_resps ) } diff --git a/R/m-backend-submit.R b/R/m-backend-submit.R index cb1db60..ef89ec0 100644 --- a/R/m-backend-submit.R +++ b/R/m-backend-submit.R @@ -36,7 +36,7 @@ m_backend_submit.mall_simulate_llm <- function(backend, x, prompt) { args <- backend class(args) <- "list" if (args$model == "pipe") { - out <- map_chr(x, \(x) trimws(strsplit(x, "\\|")[[1]][[2]])) + out <- map_chr(x, \(x) trimws(strsplit(x, "\\|")[[1]][[2]])) } else if (args$model == "prompt") { out <- glue("{prompt}\n{x}") } else if (args$model == "echo") { diff --git a/tests/testthat/test-llm-extract.R b/tests/testthat/test-llm-extract.R index 1c314b5..f3b1328 100644 --- a/tests/testthat/test-llm-extract.R +++ b/tests/testthat/test-llm-extract.R @@ -1,11 +1,11 @@ test_that("Extract works", { llm_use("simulate_llm", "echo", .silent = TRUE) - + expect_equal( llm_vec_extract("{\"product\":\"toaster\"}", labels = "product"), "toaster" ) - + entries2 <- "{\"product\":\"toaster\", \"product\":\"TV\"}" entries2_result <- "toaster|TV" expect_equal( @@ -16,25 +16,25 @@ test_that("Extract works", { entries2_result ) expect_equal( - llm_extract(data.frame(x = entries2), x, labels = "product"), + llm_extract(data.frame(x = entries2), x, labels = "product"), data.frame(x = entries2, .extract = entries2_result) ) expect_equal( llm_extract( .data = data.frame(x = entries2), - col = x, + col = x, labels = c("product1", "product2"), expand_cols = TRUE ), - data.frame(x = entries2, product1 = "toaster", product2 = "TV") + data.frame(x = entries2, product1 = "toaster", product2 = "TV") ) expect_equal( llm_extract( .data = data.frame(x = entries2), - col = x, + col = x, labels = c(y = "product1", z = "product2"), expand_cols = TRUE ), - data.frame(x = entries2, y = "toaster", z = "TV") + data.frame(x = entries2, y = "toaster", z = "TV") ) -}) \ No newline at end of file +}) diff --git a/tests/testthat/test-llm-sentiment.R b/tests/testthat/test-llm-sentiment.R index d9b0cb6..a02a20a 100644 --- a/tests/testthat/test-llm-sentiment.R +++ b/tests/testthat/test-llm-sentiment.R @@ -8,7 +8,7 @@ test_that("Sentiment works", { x <- llm_vec_sentiment("this is a test|notvalid") ) expect_equal(x, as.character(NA)) - + entries <- c("a|positive", "b|negative") expect_equal( llm_sentiment(data.frame(x = entries), x), diff --git a/tests/testthat/test-llm-translate.R b/tests/testthat/test-llm-translate.R index 2a589ec..c84359d 100644 --- a/tests/testthat/test-llm-translate.R +++ b/tests/testthat/test-llm-translate.R @@ -15,7 +15,7 @@ test_that("Translate works", { llm_translate( data.frame(x = test_text), x, - pred_name = "new", + pred_name = "new", language = "other" ), data.frame(x = test_text, new = test_text) From c08fc789814e433e46fff940900515f34d865523 Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 11:00:28 -0500 Subject: [PATCH 08/15] Removes unused test setups --- R/m-backend-submit.R | 8 -------- 1 file changed, 8 deletions(-) diff --git a/R/m-backend-submit.R b/R/m-backend-submit.R index ef89ec0..7e4c4a7 100644 --- a/R/m-backend-submit.R +++ b/R/m-backend-submit.R @@ -37,16 +37,8 @@ m_backend_submit.mall_simulate_llm <- function(backend, x, prompt) { class(args) <- "list" if (args$model == "pipe") { out <- map_chr(x, \(x) trimws(strsplit(x, "\\|")[[1]][[2]])) - } else if (args$model == "prompt") { - out <- glue("{prompt}\n{x}") } else if (args$model == "echo") { out <- x - } else { - out <- list( - x = x, - base_prompt = base_prompt, - backend = args - ) } out } From c5fa6e834b788a83e03e7ef32282c644e3f2457e Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 11:00:41 -0500 Subject: [PATCH 09/15] Adds coverage test for llm_use() --- tests/testthat/test-llm-use.R | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/testthat/test-llm-use.R diff --git a/tests/testthat/test-llm-use.R b/tests/testthat/test-llm-use.R new file mode 100644 index 0000000..d4eb0af --- /dev/null +++ b/tests/testthat/test-llm-use.R @@ -0,0 +1,12 @@ +test_that("Init code is covered", { + local_mocked_bindings( + test_connection = function() { + x <- list() + x$status_code <- 200 + x + }, + list_models = function() data.frame(name = c("model1", "model2")), + menu = function(...) 1 + ) + expect_message(llm_use(force = TRUE)) +}) From abb93ca6b26d20386dd703ba959101a9a4354fa8 Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 11:00:58 -0500 Subject: [PATCH 10/15] Adds coverage test for Ollama submit --- tests/testthat/test-m-backend-submit.R | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/testthat/test-m-backend-submit.R diff --git a/tests/testthat/test-m-backend-submit.R b/tests/testthat/test-m-backend-submit.R new file mode 100644 index 0000000..cd5e6f1 --- /dev/null +++ b/tests/testthat/test-m-backend-submit.R @@ -0,0 +1,10 @@ +test_that("Ollama code is covered", { + local_mocked_bindings( + chat = function(...) "positive" + ) + llm_use("ollama", "model", .silent = TRUE) + expect_equal( + llm_vec_sentiment("I am happy"), + "positive" + ) +}) From 9a74d1795f9572168f3299ee81cc492202e57440 Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 11:05:47 -0500 Subject: [PATCH 11/15] Formally imports pull, updates ollamar function being used in docs --- NAMESPACE | 1 + R/llm-extract.R | 2 +- R/llm-use.R | 4 ++-- R/mall.R | 2 +- man/llm_use.Rd | 4 ++-- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index a79e90c..8d8d42c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -32,6 +32,7 @@ import(glue) import(rlang) importFrom(dplyr,bind_cols) importFrom(dplyr,mutate) +importFrom(dplyr,pull) importFrom(dplyr,tibble) importFrom(jsonlite,fromJSON) importFrom(ollamar,chat) diff --git a/R/llm-extract.R b/R/llm-extract.R index bca7044..2ca49d8 100644 --- a/R/llm-extract.R +++ b/R/llm-extract.R @@ -31,7 +31,7 @@ llm_extract.data.frame <- function(.data, additional_prompt = "", pred_name = ".extract") { if (expand_cols && length(labels) > 1) { - text <- dplyr::pull(.data, {{ col }}) + text <- pull(.data, {{ col }}) resp <- llm_vec_extract( x = text, labels = labels, diff --git a/R/llm-use.R b/R/llm-use.R index 2aea9e2..f051e0d 100644 --- a/R/llm-use.R +++ b/R/llm-use.R @@ -7,8 +7,8 @@ #' @param .silent Avoids console output #' @param model The name of model supported by the back-end provider #' @param ... Additional arguments that this function will pass down to the -#' integrating function. In the case of Ollama, it will pass those argument to -#' `ollamar::generate()`. +#' integrating function. In the case of Ollama, it will pass those arguments to +#' `ollamar::chat()`. #' @param force Flag that tell the function to reset all of the settings in the #' R session #' diff --git a/R/mall.R b/R/mall.R index 051f5b9..5fa40dc 100644 --- a/R/mall.R +++ b/R/mall.R @@ -1,5 +1,5 @@ #' @importFrom ollamar chat test_connection list_models -#' @importFrom dplyr mutate tibble bind_cols +#' @importFrom dplyr mutate tibble bind_cols pull #' @importFrom utils menu #' @importFrom jsonlite fromJSON #' @import rlang diff --git a/man/llm_use.Rd b/man/llm_use.Rd index 1863085..c2c9b8a 100644 --- a/man/llm_use.Rd +++ b/man/llm_use.Rd @@ -13,8 +13,8 @@ llm_use(backend = NULL, model = NULL, ..., .silent = FALSE, force = FALSE) \item{model}{The name of model supported by the back-end provider} \item{...}{Additional arguments that this function will pass down to the -integrating function. In the case of Ollama, it will pass those argument to -\code{ollamar::generate()}.} +integrating function. In the case of Ollama, it will pass those arguments to +\code{ollamar::chat()}.} \item{.silent}{Avoids console output} From 888cd18f28f223338ac57bb5e23d74d8188678ed Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 11:23:52 -0500 Subject: [PATCH 12/15] Adds Ollama test helper, adds Ollama tests to sentiment, skips if no Ollama found --- tests/testthat/_snaps/llm-sentiment.md | 22 ++++++++++++++++++++++ tests/testthat/helper-ollama.R | 18 ++++++++++++++++++ tests/testthat/test-llm-sentiment.R | 20 ++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 tests/testthat/_snaps/llm-sentiment.md create mode 100644 tests/testthat/helper-ollama.R diff --git a/tests/testthat/_snaps/llm-sentiment.md b/tests/testthat/_snaps/llm-sentiment.md new file mode 100644 index 0000000..bf586d2 --- /dev/null +++ b/tests/testthat/_snaps/llm-sentiment.md @@ -0,0 +1,22 @@ +# Sentiment on Ollama works + + Code + llm_vec_sentiment(vec_reviews) + Output + [1] "positive" "negative" "neutral" + +--- + + Code + llm_vec_sentiment(vec_reviews, options = c("positive", "negative")) + Output + [1] "positive" "negative" "negative" + +--- + + Code + llm_vec_sentiment(vec_reviews, options = c("positive", "negative"), + additional_prompt = "Consider someone not sure as a positive comment.") + Output + [1] "positive" "negative" "positive" + diff --git a/tests/testthat/helper-ollama.R b/tests/testthat/helper-ollama.R new file mode 100644 index 0000000..801bdc2 --- /dev/null +++ b/tests/testthat/helper-ollama.R @@ -0,0 +1,18 @@ +skip_if_no_ollama <- function() { + con <- ollamar::test_connection() + if (con$status_code != 200) { + skip("No Ollama found") + } +} + +reviews_vec <- function() { + c( + "This has been the best TV I've ever used. Great screen, and sound.", + "I regret buying this laptop. It is too slow and the keyboard is too noisy", + "Not sure how to feel about my new washing machine. Great color, but hard to figure" + ) +} + +reviews_table <- function() { + data.frame(reviews = reviews_vec()) +} diff --git a/tests/testthat/test-llm-sentiment.R b/tests/testthat/test-llm-sentiment.R index a02a20a..59f2ec1 100644 --- a/tests/testthat/test-llm-sentiment.R +++ b/tests/testthat/test-llm-sentiment.R @@ -19,3 +19,23 @@ test_that("Sentiment works", { data.frame(x = entries, new = c("positive", "negative")) ) }) + +test_that("Sentiment on Ollama works", { + skip_if_no_ollama() + vec_reviews <- reviews_vec() + llm_use("ollama", "llama3.1", seed = 100, .silent = TRUE) + expect_snapshot(llm_vec_sentiment(vec_reviews)) + expect_snapshot( + llm_vec_sentiment( + vec_reviews, + options = c("positive", "negative") + ) + ) + expect_snapshot( + llm_vec_sentiment( + vec_reviews, + options = c("positive", "negative"), + additional_prompt = "Consider someone not sure as a positive comment." + ) + ) +}) From b5e9fce55376595ad59e20b60647b34e8b3a605d Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 12:12:30 -0500 Subject: [PATCH 13/15] Adds classify Ollama tests, adds table tests to sentiment --- tests/testthat/_snaps/llm-classify.md | 44 ++++++++++++++++++++++++++ tests/testthat/_snaps/llm-sentiment.md | 28 ++++++++++++++++ tests/testthat/helper-ollama.R | 4 ++- tests/testthat/test-llm-classify.R | 29 +++++++++++++++++ tests/testthat/test-llm-sentiment.R | 4 ++- 5 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 tests/testthat/_snaps/llm-classify.md diff --git a/tests/testthat/_snaps/llm-classify.md b/tests/testthat/_snaps/llm-classify.md new file mode 100644 index 0000000..650d1b4 --- /dev/null +++ b/tests/testthat/_snaps/llm-classify.md @@ -0,0 +1,44 @@ +# Classify on Ollama works + + Code + llm_classify(reviews, review, labels = c("appliance", "computer")) + Output + review + 1 This has been the best TV I've ever used. Great screen, and sound. + 2 I regret buying this laptop. It is too slow and the keyboard is too noisy + 3 Not sure how to feel about my new washing machine. Great color, but hard to figure + .classify + 1 appliance + 2 computer + 3 appliance + +--- + + Code + llm_classify(reviews, review, pred_name = "new", labels = c("appliance", + "computer")) + Output + review + 1 This has been the best TV I've ever used. Great screen, and sound. + 2 I regret buying this laptop. It is too slow and the keyboard is too noisy + 3 Not sure how to feel about my new washing machine. Great color, but hard to figure + new + 1 appliance + 2 computer + 3 appliance + +--- + + Code + llm_classify(reviews, review, pred_name = "new", labels = c("appliance", + "computer"), additional_prompt = "Consider all laptops as appliances.") + Output + review + 1 This has been the best TV I've ever used. Great screen, and sound. + 2 I regret buying this laptop. It is too slow and the keyboard is too noisy + 3 Not sure how to feel about my new washing machine. Great color, but hard to figure + new + 1 appliance + 2 appliance + 3 appliance + diff --git a/tests/testthat/_snaps/llm-sentiment.md b/tests/testthat/_snaps/llm-sentiment.md index bf586d2..6076166 100644 --- a/tests/testthat/_snaps/llm-sentiment.md +++ b/tests/testthat/_snaps/llm-sentiment.md @@ -20,3 +20,31 @@ Output [1] "positive" "negative" "positive" +--- + + Code + llm_sentiment(reviews, review) + Output + review + 1 This has been the best TV I've ever used. Great screen, and sound. + 2 I regret buying this laptop. It is too slow and the keyboard is too noisy + 3 Not sure how to feel about my new washing machine. Great color, but hard to figure + .sentiment + 1 positive + 2 negative + 3 neutral + +--- + + Code + llm_sentiment(reviews, review, pred_name = "new") + Output + review + 1 This has been the best TV I've ever used. Great screen, and sound. + 2 I regret buying this laptop. It is too slow and the keyboard is too noisy + 3 Not sure how to feel about my new washing machine. Great color, but hard to figure + new + 1 positive + 2 negative + 3 neutral + diff --git a/tests/testthat/helper-ollama.R b/tests/testthat/helper-ollama.R index 801bdc2..3d4cac8 100644 --- a/tests/testthat/helper-ollama.R +++ b/tests/testthat/helper-ollama.R @@ -2,6 +2,8 @@ skip_if_no_ollama <- function() { con <- ollamar::test_connection() if (con$status_code != 200) { skip("No Ollama found") + } else { + llm_use("ollama", "llama3.1", seed = 100, .silent = TRUE) } } @@ -14,5 +16,5 @@ reviews_vec <- function() { } reviews_table <- function() { - data.frame(reviews = reviews_vec()) + data.frame(review = reviews_vec()) } diff --git a/tests/testthat/test-llm-classify.R b/tests/testthat/test-llm-classify.R index 7c44c5e..27b2f70 100644 --- a/tests/testthat/test-llm-classify.R +++ b/tests/testthat/test-llm-classify.R @@ -25,3 +25,32 @@ test_that("Classify works", { data.frame(x = test_text, new = test_text) ) }) + +test_that("Classify on Ollama works", { + skip_if_no_ollama() + reviews <- reviews_table() + expect_snapshot( + llm_classify( + reviews, + review, + labels = c("appliance", "computer") + ) + ) + expect_snapshot( + llm_classify( + reviews, + review, + pred_name = "new", + labels = c("appliance", "computer") + ) + ) + expect_snapshot( + llm_classify( + reviews, + review, + pred_name = "new", + labels = c("appliance", "computer"), + additional_prompt = "Consider all laptops as appliances." + ) + ) +}) diff --git a/tests/testthat/test-llm-sentiment.R b/tests/testthat/test-llm-sentiment.R index 59f2ec1..22ee7a1 100644 --- a/tests/testthat/test-llm-sentiment.R +++ b/tests/testthat/test-llm-sentiment.R @@ -23,7 +23,7 @@ test_that("Sentiment works", { test_that("Sentiment on Ollama works", { skip_if_no_ollama() vec_reviews <- reviews_vec() - llm_use("ollama", "llama3.1", seed = 100, .silent = TRUE) + reviews <- reviews_table() expect_snapshot(llm_vec_sentiment(vec_reviews)) expect_snapshot( llm_vec_sentiment( @@ -38,4 +38,6 @@ test_that("Sentiment on Ollama works", { additional_prompt = "Consider someone not sure as a positive comment." ) ) + expect_snapshot(llm_sentiment(reviews, review)) + expect_snapshot(llm_sentiment(reviews, review, pred_name = "new")) }) From 7b27f4c02033e989af309dfd57b6a8b7d5b7dcfb Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 12:38:41 -0500 Subject: [PATCH 14/15] Adds basic Ollama tests to others --- tests/testthat/_snaps/llm-custom.md | 14 ++++++++++++++ tests/testthat/_snaps/llm-extract.md | 14 ++++++++++++++ tests/testthat/_snaps/llm-summarize.md | 14 ++++++++++++++ tests/testthat/_snaps/llm-translate.md | 14 ++++++++++++++ tests/testthat/test-llm-custom.R | 11 +++++++++++ tests/testthat/test-llm-extract.R | 5 +++++ tests/testthat/test-llm-summarize.R | 5 +++++ tests/testthat/test-llm-translate.R | 5 +++++ 8 files changed, 82 insertions(+) create mode 100644 tests/testthat/_snaps/llm-custom.md create mode 100644 tests/testthat/_snaps/llm-extract.md create mode 100644 tests/testthat/_snaps/llm-summarize.md create mode 100644 tests/testthat/_snaps/llm-translate.md diff --git a/tests/testthat/_snaps/llm-custom.md b/tests/testthat/_snaps/llm-custom.md new file mode 100644 index 0000000..5e1342a --- /dev/null +++ b/tests/testthat/_snaps/llm-custom.md @@ -0,0 +1,14 @@ +# Custom on Ollama works + + Code + llm_custom(reviews_table(), review, my_prompt) + Output + review + 1 This has been the best TV I've ever used. Great screen, and sound. + 2 I regret buying this laptop. It is too slow and the keyboard is too noisy + 3 Not sure how to feel about my new washing machine. Great color, but hard to figure + .pred + 1 Yes + 2 No + 3 No + diff --git a/tests/testthat/_snaps/llm-extract.md b/tests/testthat/_snaps/llm-extract.md new file mode 100644 index 0000000..4da22b7 --- /dev/null +++ b/tests/testthat/_snaps/llm-extract.md @@ -0,0 +1,14 @@ +# Extract on Ollama works + + Code + llm_extract(reviews_table(), review, "product") + Output + review + 1 This has been the best TV I've ever used. Great screen, and sound. + 2 I regret buying this laptop. It is too slow and the keyboard is too noisy + 3 Not sure how to feel about my new washing machine. Great color, but hard to figure + .extract + 1 tv + 2 laptop + 3 washing machine + diff --git a/tests/testthat/_snaps/llm-summarize.md b/tests/testthat/_snaps/llm-summarize.md new file mode 100644 index 0000000..ad13916 --- /dev/null +++ b/tests/testthat/_snaps/llm-summarize.md @@ -0,0 +1,14 @@ +# Summarize on Ollama works + + Code + llm_summarize(reviews_table(), review, max_words = 5) + Output + review + 1 This has been the best TV I've ever used. Great screen, and sound. + 2 I regret buying this laptop. It is too slow and the keyboard is too noisy + 3 Not sure how to feel about my new washing machine. Great color, but hard to figure + .summary + 1 very good tv experience overall + 2 slow and noisy laptop purchase + 3 mixed feelings about new washer + diff --git a/tests/testthat/_snaps/llm-translate.md b/tests/testthat/_snaps/llm-translate.md new file mode 100644 index 0000000..b319411 --- /dev/null +++ b/tests/testthat/_snaps/llm-translate.md @@ -0,0 +1,14 @@ +# Translate on Ollama works + + Code + llm_translate(reviews_table(), review, "spanish") + Output + review + 1 This has been the best TV I've ever used. Great screen, and sound. + 2 I regret buying this laptop. It is too slow and the keyboard is too noisy + 3 Not sure how to feel about my new washing machine. Great color, but hard to figure + .translation + 1 Este ha sido el mejor televisor que he utilizado. Gran pantalla y sonido. + 2 Lamento haber comprado esta laptop. Está demasiado lenta y el teclado es demasiado ruidoso. + 3 No estoy seguro de cómo sentirme sobre mi nueva lavadora. Color genial, pero difícil de manejar. + diff --git a/tests/testthat/test-llm-custom.R b/tests/testthat/test-llm-custom.R index fdfc9a5..1f79531 100644 --- a/tests/testthat/test-llm-custom.R +++ b/tests/testthat/test-llm-custom.R @@ -20,3 +20,14 @@ test_that("Custom works", { data.frame(x = test_text, new = test_text) ) }) + +test_that("Custom on Ollama works",{ + skip_if_no_ollama() + my_prompt <- paste( + "Answer a question.", + "Return only the answer, no explanation", + "Acceptable answers are 'yes', 'no'", + "Answer this about the following text, is this a happy customer?:" + ) + expect_snapshot(llm_custom(reviews_table(), review, my_prompt)) +}) diff --git a/tests/testthat/test-llm-extract.R b/tests/testthat/test-llm-extract.R index f3b1328..14648f6 100644 --- a/tests/testthat/test-llm-extract.R +++ b/tests/testthat/test-llm-extract.R @@ -38,3 +38,8 @@ test_that("Extract works", { data.frame(x = entries2, y = "toaster", z = "TV") ) }) + +test_that("Extract on Ollama works",{ + skip_if_no_ollama() + expect_snapshot(llm_extract(reviews_table(), review, "product")) +}) diff --git a/tests/testthat/test-llm-summarize.R b/tests/testthat/test-llm-summarize.R index 2ad1966..bb84344 100644 --- a/tests/testthat/test-llm-summarize.R +++ b/tests/testthat/test-llm-summarize.R @@ -20,3 +20,8 @@ test_that("Summarize works", { data.frame(x = test_text, new = test_text) ) }) + +test_that("Summarize on Ollama works",{ + skip_if_no_ollama() + expect_snapshot(llm_summarize(reviews_table(), review, max_words = 5)) +}) \ No newline at end of file diff --git a/tests/testthat/test-llm-translate.R b/tests/testthat/test-llm-translate.R index c84359d..b9fb4aa 100644 --- a/tests/testthat/test-llm-translate.R +++ b/tests/testthat/test-llm-translate.R @@ -21,3 +21,8 @@ test_that("Translate works", { data.frame(x = test_text, new = test_text) ) }) + +test_that("Translate on Ollama works",{ + skip_if_no_ollama() + expect_snapshot(llm_translate(reviews_table(), review, "spanish")) +}) \ No newline at end of file From c775af93bbbe39ef572c2e532e2ce7f474cb089e Mon Sep 17 00:00:00 2001 From: Edgar Ruiz Date: Thu, 12 Sep 2024 12:40:24 -0500 Subject: [PATCH 15/15] Adds checks CI job --- .github/workflows/R-CMD-check.yaml | 52 ++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 .github/workflows/R-CMD-check.yaml diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml new file mode 100644 index 0000000..c914ce4 --- /dev/null +++ b/.github/workflows/R-CMD-check.yaml @@ -0,0 +1,52 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: main + pull_request: + branches: main + +name: R-CMD-check.yaml + +permissions: read-all + +jobs: + R-CMD-check: + runs-on: ${{ matrix.config.os }} + + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + + strategy: + fail-fast: false + matrix: + config: + - {os: macos-latest, r: 'release'} + - {os: windows-latest, r: 'release'} + - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} + - {os: ubuntu-latest, r: 'release'} + - {os: ubuntu-latest, r: 'oldrel-1'} + + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::rcmdcheck + needs: check + + - uses: r-lib/actions/check-r-package@v2 + with: + upload-snapshots: true + build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")'