diff --git a/.Rbuildignore b/.Rbuildignore index f0a5d34..e266d62 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -6,3 +6,5 @@ ^docs$ ^pkgdown$ ^\.github$ +^README\.qmd$ +^tidychatmodels\.png$ diff --git a/DESCRIPTION b/DESCRIPTION index d3c9467..81afab4 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,8 +2,10 @@ Type: Package Package: tidychatmodels Title: Chat With All Kinds of AI Models Through a Common Interface Version: 0.1.0 -Authors@R: - person("Albert", "Rapp", , "info@albert-rapp.de", role = c("aut", "cre")) +Authors@R: c( + person("Albert", "Rapp", , "info@albert-rapp.de", role = c("aut", "cre")), + person("John", "Coene", , "jcoenep@gmail.com", role = c("ctb")) + ) Description: This packages lets you chat with models from openAI and mistral.ai really easily. This package is set up in a modular fashion (similar to {tidymodels}) so that it is easy to switch between using @@ -16,7 +18,6 @@ Depends: R (>= 4.1.0) Imports: cli, - glue, httr2, knitr, purrr, diff --git a/NAMESPACE b/NAMESPACE index 65dd9f6..a35cf5b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,9 +1,41 @@ # Generated by roxygen2: do not edit by hand -S3method(print,chat) +S3method(add_message,tidychat) +S3method(add_params,tidychat) +S3method(append_message,antropic) +S3method(append_message,ollama) +S3method(append_message,tidychat) +S3method(extract_chat,tidychat) +S3method(get_engine,tidychat) +S3method(get_messages,tidychat) +S3method(get_model,tidychat) +S3method(get_params,tidychat) +S3method(get_uses,tidychat) +S3method(inc_uses,tidychat) +S3method(perform_chat,tidychat) +S3method(perform_query,tidychat) +S3method(prepare_engine,anthropic) +S3method(prepare_engine,tidychat) +S3method(print,tidychat) +S3method(set_uses,tidychat) export(add_message) export(add_model) export(add_params) +export(append_message) export(create_chat) export(extract_chat) +export(get_engine) +export(get_messages) +export(get_model) +export(get_params) +export(get_uses) +export(inc_uses) +export(new_chat) +export(new_chat_anthropic) +export(new_chat_mistral) +export(new_chat_ollama) +export(new_chat_openai) export(perform_chat) +export(perform_query) +export(prepare_engine) +export(set_uses) diff --git a/R/add_message.R b/R/add_message.R index 3a1a42a..cc9da39 100644 --- a/R/add_message.R +++ b/R/add_message.R @@ -1,11 +1,10 @@ #' Add messages to a chat object. #' -#' @param chat_obj A chat object created from `create_chat()` +#' @param chat A chat object of class `tidychat`. #' @param message A character vector with one element. The message to add to the chat. #' @param role A character vector with one element. The role of the message. Typically 'user' or 'system'. #' #' @return A chat object with the messages added -#' @export #' #' @examples #' \dontrun{dotenv::load_dot_env() @@ -31,13 +30,72 @@ #' ) |> #' add_message('2 + 2 is 4, minus 1 that\'s 3, ') #' } -add_message <- function(chat_obj, message, role = 'user') { +#' @export +#' @name add_message +add_message <- function(chat, message, role = "user") UseMethod("add_message") + +#' @describeIn add_message Add a message to a `tidychat` object. +#' @export +add_message.tidychat <- function(chat, message, role = "user") { + messages <- get_messages(chat) - chat_obj$messages[[length(chat_obj$messages) + 1]] <- list( + messages[[length(messages) + 1]] <- list( role = role, content = message ) - chat_obj + + attr(chat, "messages") <- messages + chat +} + +#' Get messages from a chat object. +#' @param chat An object of class `tidychat`. +#' @export +#' @name get_messages +get_messages <- function(chat) UseMethod("get_messages") + +#' @describeIn get_messages Get messages from a `tidychat` object. +#' @export +get_messages.tidychat <- function(chat) { + attr(chat, "messages") +} + +#' Append a message to a `tidychat` object. +#' @param chat An object of class `tidychat`. +#' @param response The response as returned by `perform_query`. +#' @param ... Ignored for future compatibility. +#' @export +#' @name append_message +append_message <- function(chat, response, ...) UseMethod("append_message") + +#' @describeIn append_message Appends a message to a `tidychat` object. +#' @export +append_message.tidychat <- function(chat, response, ...) { + messages <- get_messages(chat) + + messages[[length(messages) + 1]] <- response$choices[[1]]$message + + attr(chat, "messages") <- messages + invisible(chat) } +#' @describeIn append_message Appends a message to a `ollama` object. +#' @export +append_message.ollama <- function(chat, response, ...){ + messages <- get_messages(chat) + messages[[length(messages) + 1]] <- response$message + attr(chat, "messages") <- messages + invisible(chat) +} +#' @describeIn append_message Appends a message to a `anthropic` object. +#' @export +append_message.antropic <- function(chat, response, ...){ + messages <- get_messages(chat) + messages[[length(messages) + 1]] <- list( + role = "assistant", + content = response$content[[1]]$text + ) + attr(chat, "messages") <- messages + invisible(chat) +} diff --git a/R/add_model.R b/R/add_model.R index 46843b1..3ff6844 100644 --- a/R/add_model.R +++ b/R/add_model.R @@ -1,10 +1,12 @@ #' Add a model to a chat object. #' -#' @param chat_obj A chat object created from `create_chat()` -#' @param model A character vector with one element. The model to use for the chat. You can use any chat completion model from openAI and mistral.ai here. Refer to their API docs for specific names. +#' @param chat An object of class `tidychat`. +#' @param model A character vector with one element. +#' The model to use for the chat. +#' You can use any chat completion model from openAI and mistral.ai here. +#' Refer to their API docs for specific names. #' #' @return A chat object with the model added -#' @export #' #' @examples #' \dontrun{ @@ -15,8 +17,21 @@ #' chat_mistral <- create_chat('mistral', Sys.getenv('MISTRAL_DEV_KEY')) |> #' add_model('mistral-large-latest') #' } -add_model <- function(chat_obj, model) { - chat_obj$model <- model - chat_obj +#' @export +#' @name add_model +add_model <- function(chat, model) { + attr(chat, "model") <- model + chat } +#' Get model from a chat object. +#' @param chat An object of class `tidychat`. +#' @export +#' @name get_model +get_model <- function(chat) UseMethod("get_model") + +#' @describeIn get_model Gets a model from a `tidychat` object. +#' @export +get_model.tidychat <- function(chat) { + attr(chat, "model") +} diff --git a/R/add_params.R b/R/add_params.R index e3fdfce..09fd850 100644 --- a/R/add_params.R +++ b/R/add_params.R @@ -4,7 +4,6 @@ #' @param ... A named list of parameters to add to the chat object. Must be valid parameters from the API documentation. #' #' @return A chat object with the parameters added -#' @export #' #' @examples #' \dontrun{dotenv::load_dot_env() @@ -16,16 +15,26 @@ #' add_model('mistral-large-latest') |> #' add_params('temperature' = 0.5, 'max_tokens' = 100) #' } -add_params <- function(chat_obj, ...) { - if (is.null(chat_obj$params)) { - chat_obj$params <- utils::modifyList(list(), list(...)) - } else { - chat_obj$params <- utils::modifyList(chat_obj$params, list(...)) - } - chat_obj -} - - +#' @export +#' @name add_params +add_params <- function(chat, ...) UseMethod("add_params") +#' @describeIn add_params Add parameters to a `tidychat` object. +#' @export +add_params.tidychat <- function(chat, ...) { + params <- modifyList(get_params(chat), list(...)) + attr(chat, "params") <- params + chat +} +#' Get parameters from a chat object. +#' @param chat An object of class `tidychat`. +#' @export +#' @name get_params +get_params <- function(chat) UseMethod("get_params") +#' @describeIn get_params Add parameters to a `tidychat` object. +#' @export +get_params.tidychat <- function(chat) { + attr(chat, "params") +} diff --git a/R/create_chat.R b/R/create_chat.R index f54b3d4..adafda2 100644 --- a/R/create_chat.R +++ b/R/create_chat.R @@ -1,91 +1,53 @@ #' Create a chat object. #' -#' @param vendor A character vector with one element. Currently, only 'openai', 'mistral', 'anthropic' and 'ollama' are supported. -#' @param api_key The API key for the vendor's chat engine. If the vendor is 'ollama', this parameter is not required. -#' @param port The port number for the ollama chat engine. Default to ollama's standard port. If the vendor is not 'ollama', this parameter is not required. -#' @param api_version Api version that is required for Anthropic +#' @param vendor,engine A character vector with one element. +#' Currently, only "openai", "mistral", "anthropic" and "ollama" are supported. +#' @param api_key,key The API key for the vendor"s chat engine. +#' If the vendor is "ollama", this parameter is not required. +#' @param port The port number for the ollama chat engine. +#' Default to ollama"s standard port. If the vendor is not "ollama", this parameter is not required. +#' @param api_version,version Api version that is required for Anthropic +#' @param ... Additional parameters to be passed to the chat engine query. +#' @param object New query object from [httr2::request]. #' #' @return A chat object -#' @export #' #' @examples #' \dontrun{ #' dotenv::load_dot_env() -#' chat_openai <- create_chat('openai', Sys.getenv('OAI_DEV_KEY')) -#' chat_mistral <- create_chat('mistral', Sys.getenv('MISTRAL_DEV_KEY')) +#' chat_openai <- create_chat("openai", Sys.getenv("OAI_DEV_KEY")) +#' chat_mistral <- create_chat("mistral", Sys.getenv("MISTRAL_DEV_KEY")) #' } -create_chat <- function(vendor, api_key = '', port = if (vendor == 'ollama') 11434 else NULL, api_version = '') { - if (vendor != 'openai' & vendor != 'mistral' & vendor != 'ollama' & vendor != 'anthropic') stop('Unsupported vendor') - - if (vendor == 'openai') { - # https://platform.openai.com/docs/api-reference/making-requests - engine <- httr2::request( - base_url ='https://api.openai.com/v1/chat/completions' - ) |> - httr2::req_headers( - 'Authorization' = paste('Bearer', api_key), - 'Content-Type' = 'application/json' - ) - } - - if (vendor == 'mistral') { - # https://docs.mistral.ai/ - engine <- httr2::request( - base_url ='https://api.mistral.ai/v1/chat/completions' - ) |> - httr2::req_headers( - 'Authorization' = paste('Bearer', api_key), - 'Content-Type' = 'application/json', - 'Accept' = 'application/json' - ) - } - - if (vendor == 'ollama') { - # https://docs.mistral.ai/ - engine <- httr2::request( - base_url = glue::glue( - 'http://localhost:{port}/api/chat' - ) - ) - } - - if (vendor == 'anthropic') { - # https://platform.openai.com/docs/api-reference/making-requests - if (api_version == '') stop('Anthropic requires API version') - - engine <- httr2::request( - base_url ='https://api.anthropic.com/v1/messages' - ) |> - httr2::req_headers( - 'x-api-key' = api_key, - 'Content-Type' = 'application/json', - 'anthropic-version' = api_version - ) - } - +#' @export +#' @name create_chat +create_chat <- function( + vendor = c("openai", "mistral", "ollama", "anthropic"), + api_key = "", + port = if (vendor == "ollama") 11434 else NULL, + api_version = "" +) { + vendor <- match.arg(vendor) - if (vendor == 'ollama') { - chat <- list( - vendor_name = vendor, - engine = engine, - messages = list(), - params = list(stream = FALSE) + .Deprecated( + call, + "tidychat", + msg = sprintf( + "Deprecated in favour of `new_chat_%s`", + vendor ) - } + ) - if (vendor != 'ollama') { - chat <- list( - vendor_name = vendor, - engine = engine, - messages = list() - ) - } - class(chat) <- 'chat' - return(chat) + switch( + vendor, + openai = new_chat_openai(api_key), + mistral = new_chat_mistral(api_key), + ollama = new_chat_ollama(port), + anthropic = new_chat_anthropic(api_key, api_version) + ) } #' @export -print.chat <- function(x, ...) { +print.tidychat <- function(x, ...) { cli::cli_div( theme = list( span.param = list(color = "blue") @@ -110,3 +72,83 @@ print.chat <- function(x, ...) { knit_print.chat <- function(x, ...) { knitr::knit_print(x, ...) } + +#' @export +#' @rdname create_chat +new_chat <- function(engine, object, ...){ + structure( + object, + engine = engine, + messages = list(), + params = list(...), + model = NULL, + uses = 0L, + class = c("tidychat", engine, class(object)) + ) +} + +#' @export +#' @rdname create_chat +new_chat_openai <- function(key = Sys.getenv("OPENAI_API_KEY")){ + stopifnot(key != "") + # https://platform.openai.com/docs/api-reference/making-requests + req <- httr2::request( + base_url = "https://api.openai.com/v1/chat/completions" + ) |> + httr2::req_headers( + "Authorization" = paste("Bearer", key), + "Content-Type" = "application/json" + ) + + new_chat("openai", req) +} + +#' @export +#' @rdname create_chat +new_chat_mistral <- function(key = Sys.getenv("MISTRAL_API_KEY")){ + stopifnot(key != "") + # https://docs.mistral.ai/ + req <- httr2::request( + base_url = "https://api.mistral.ai/v1/chat/completions" + ) |> + httr2::req_headers( + "Authorization" = paste("Bearer", key), + "Content-Type" = "application/json", + "Accept" = "application/json" + ) + + new_chat("mistral", req) +} + +#' @export +#' @rdname create_chat +new_chat_ollama <- function(port) { + stopifnot(!missing(port)) + # https://docs.mistral.ai/ + req <- httr2::request( + base_url = sprintf( + "http://localhost:%s/api/chat", + port + ) + ) + + new_chat("ollama", req, stream = FALSE) +} + +#' @export +#' @rdname create_chat +new_chat_anthropic <- function(key, version){ + stopifnot(!missing(key), !missing(version)) + # https://platform.openai.com/docs/api-reference/making-requests + + req <- httr2::request( + base_url = "https://api.anthropic.com/v1/messages" + ) |> + httr2::req_headers( + "x-api-key" = key, + "Content-Type" = "application/json", + "anthropic-version" = version + ) + + new_chat("anthropic", req) +} diff --git a/R/extract_chat.R b/R/extract_chat.R index 182d38d..cb6d329 100644 --- a/R/extract_chat.R +++ b/R/extract_chat.R @@ -1,10 +1,9 @@ #' Prints all messages to the console and saves them invisibly #' -#' @param chat_obj A chat object created from `create_chat()` +#' @param chat A chat object created from `create_chat()` #' @param silent A logical vector with one element. If TRUE, the messages are not printed to the console. #' #' @return A chat object with the responses added -#' @export #' #' @examples #' \dontrun{dotenv::load_dot_env() @@ -36,7 +35,13 @@ #' perform_chat() #' msgs_mistral <- chat_mistral |> extract_chat() #' } -extract_chat <- function(chat_obj, silent = FALSE) { +#' @export +#' @name extract_chat +extract_chat <- function(chat, silent = FALSE) UseMethod("extract_chat") + +#' @describeIn extract_chat Extracts the chat messages from a `tidychat` object. +#' @export +extract_chat.tidychat <- function(chat, silent = FALSE) { if (!silent) { cli::cli_div( theme = list( @@ -46,21 +51,22 @@ extract_chat <- function(chat_obj, silent = FALSE) { ) ) - for (i in seq_along(chat_obj$messages)) { - if (chat_obj$messages[[i]]$role == "system") { - cli::cli_text("{.system_msg System: {chat_obj$messages[[i]]$content}}") + for (msg in get_messages(chat)) { + if (msg$role == "system") { + cli::cli_text("{.system_msg System: {chat$messages[[i]]$content}}") } - if (chat_obj$messages[[i]]$role == "user") { - cli::cli_text("{.user_msg User: {chat_obj$messages[[i]]$content}}") + if (msg$role == "user") { + cli::cli_text("{.user_msg User: {chat$messages[[i]]$content}}") } - if (chat_obj$messages[[i]]$role == "assistant") { - cli::cli_text("{.assistant_msg Assistant: {chat_obj$messages[[i]]$content}}") + if (msg[[i]]$role == "assistant") { + cli::cli_text("{.assistant_msg Assistant: {chat$messages[[i]]$content}}") } } } - transposed_and_flattened_chats <- chat_obj$messages |> + + transposed_and_flattened_chats <- get_messages(chat) |> purrr::transpose() |> purrr::map(unlist) @@ -68,9 +74,9 @@ extract_chat <- function(chat_obj, silent = FALSE) { role = transposed_and_flattened_chats$role, message = transposed_and_flattened_chats$content ) - if (!silent) { + + if (!silent) return(invisible(msg_tibble)) - } else { - return(msg_tibble) - } + + return(msg_tibble) } diff --git a/R/perform_chat.R b/R/perform_chat.R index 3bd15a9..27af5b0 100644 --- a/R/perform_chat.R +++ b/R/perform_chat.R @@ -1,10 +1,11 @@ #' Sends the chat to the engine and adds the response to the chat object #' -#' @param chat_obj A chat object created from `create_chat()` -#' @param dry_run A logical indicating whether to return the prepared `httr2` request without actually sending out the request to the vendor. Defaults to FALSE. +#' @param chat An object of class `tidychat`. +#' @param dry_run A logical indicating whether to return the prepared_engine +#' `httr2` request without actually sending out the request to the vendor. Defaults to FALSE. +#' @param ... Ignored for future compatibility. #' #' @return A chat object with the responses added -#' @export #' #' @examples #' \dontrun{dotenv::load_dot_env() @@ -34,73 +35,97 @@ #' chat_mistral <- chat_mistral |> #' perform_chat() #' } -perform_chat <- function(chat_obj, dry_run = FALSE) { +#' +#' @export +#' @name perform_chat +perform_chat <- function(chat, dry_run = FALSE, ...) UseMethod("perform_chat") - if (chat_obj$vendor_name == 'anthropic') { - msgs <- chat_obj$messages +#' @describeIn perform_chat Perform a chat on a `tidychat` object. +#' @export +perform_chat.tidychat <- function(chat, dry_run = FALSE, ...){ + # prepare the query + prepared <- prepare_engine(chat) - non_system_msgs <- msgs[purrr::map_lgl(msgs, \(x) x$role != 'system')] - system_msg <- msgs[purrr::map_lgl(msgs, \(x) x$role == 'system')] - if (length(system_msg) > 1) stop('There can only be one system message') + # return early if dry run + if (dry_run) return(prepared) - if (length(system_msg) == 0) { - prepared_engine <- chat_obj$engine |> - httr2::req_body_json( - data = rlang::list2( - messages = non_system_msgs, - model = chat_obj$model, - !!!chat_obj$params - ) - ) - } + # perform the query + response <- perform_query(chat, prepared) - if (length(system_msg) == 1) { - prepared_engine <- chat_obj$engine |> - httr2::req_body_json( - data = rlang::list2( - messages = non_system_msgs, - model = chat_obj$model, - system = system_msg[[1]]$content, - !!!chat_obj$params - ) - ) - } + # increment the uses by 1 + chat <- inc_uses(chat) - } + # post process the query + chat <- append_message(chat, response) + + invisible(chat) +} + +#' Prepare tidychat engine query +#' @param chat An object of class `tidychat`. +#' @param ... Ignored for future compatibility. +#' @export +#' @name prepare_engine +prepare_engine <- function(chat, ...) UseMethod("prepare_engine") + +#' @describeIn prepare_engine Prepare a tidychat engine query. +#' @export +prepare_engine.tidychat <- function(chat, ...) { + chat |> + httr2::req_body_json( + data = rlang::list2( + messages = get_messages(chat), + model = get_model(chat), + !!!get_params(chat) + ) + ) +} + +#' @describeIn prepare_engine Prepare an anthropic engine query. +#' @export +prepare_engine.anthropic <- function(chat, ...){ + msgs <- get_messages(chat) + + non_system_msgs <- msgs[purrr::map_lgl(msgs, \(x) x$role != "system")] + system_msg <- msgs[purrr::map_lgl(msgs, \(x) x$role == "system")] - if (chat_obj$vendor_name != 'anthropic') { - prepared_engine <- chat_obj$engine |> + if (length(system_msg) > 1) stop("There can only be one system message") + + if (length(system_msg) == 0) { + prepared_engine <- get_engine(chat) |> httr2::req_body_json( data = rlang::list2( - messages = chat_obj$messages, - model = chat_obj$model, - !!!chat_obj$params + messages = non_system_msgs, + model = get_model(chat), + !!!get_params(chat) ) ) + return(prepared_engine) } + get_engine(chat) |> + httr2::req_body_json( + data = rlang::list2( + messages = non_system_msgs, + model = get_model(chat), + system = system_msg[[1]]$content, + !!!get_params(chat) + ) + ) +} - if (dry_run) return(prepared_engine) +#' Perform a tidychat engine query +#' @param chat An object of class `tidychat`. +#' @param prepared_query A prepared query as returned by `prepare_engine`. +#' @param ... Ignored for future compatibility. +#' @export +#' @name perform_query +perform_query <- function(chat, prepared_query, ...) UseMethod("perform_query") - response <- prepared_engine |> +#' @describeIn perform_query Perform a tidychat engine query. +#' @export +perform_query.tidychat <- function(chat, prepared_query, ...){ + prepared_query |> httr2::req_perform() |> httr2::resp_body_json() - - if (chat_obj$vendor_name == 'ollama') { - chat_obj$messages[[length(chat_obj$messages) + 1]] <- response$message - } - - if (chat_obj$vendor_name == 'anthropic') { - chat_obj$messages[[length(chat_obj$messages) + 1]] <- list( - role = 'assistant', - content = response$content[[1]]$text - ) - } - - if (chat_obj$vendor_name != 'ollama') { - chat_obj$messages[[length(chat_obj$messages) + 1]] <- response$choices[[1]]$message - } - chat_obj$usage[[length(chat_obj$usage) + 1]] <- response$usage - chat_obj } - diff --git a/R/utils.R b/R/utils.R new file mode 100644 index 0000000..c0e5c4a --- /dev/null +++ b/R/utils.R @@ -0,0 +1,52 @@ +#' Get params from a chat object +#' @param chat An object of class `tidychat`. +#' @export +get_params <- function(chat) UseMethod("get_params") + +#' @export +get_params.tidychat <- function(chat) { + attr(chat, "params") +} + +#' Get uses from a chat object +#' @param chat An object of class `tidychat`. +#' @export +get_uses <- function(chat) UseMethod("get_uses") + +#' @export +get_uses.tidychat <- function(chat) { + attr(chat, "uses") +} + +#' Set uses from a chat object +#' @param chat An object of class `tidychat`. +#' @param value An integer to set the uses to. +#' @export +set_uses <- function(chat, value) UseMethod("set_uses") + +#' @export +set_uses.tidychat <- function(chat, value) { + attr(chat, "uses") <- value +} + +#' Increment uses from a chat object by 1 +#' @param chat An object of class `tidychat`. +#' @export +inc_uses <- function(chat) UseMethod("inc_uses") + +#' @export +inc_uses.tidychat <- function(chat) { + uses <- attr(chat, "uses") + attr(chat, "uses") <- uses + 1 + return(chat) +} + +#' Get engine from a `tidychat` object +#' @param chat An object of class `tidychat`. +#' @export +get_engine <- function(chat) UseMethod("get_engine") + +#' @export +get_engine.tidychat <- function(chat) { + attr(chat, "engine") +} diff --git a/man/add_message.Rd b/man/add_message.Rd index c821e93..0694c71 100644 --- a/man/add_message.Rd +++ b/man/add_message.Rd @@ -2,12 +2,15 @@ % Please edit documentation in R/add_message.R \name{add_message} \alias{add_message} +\alias{add_message.tidychat} \title{Add messages to a chat object.} \usage{ -add_message(chat_obj, message, role = "user") +add_message(chat, message, role = "user") + +\method{add_message}{tidychat}(chat, message, role = "user") } \arguments{ -\item{chat_obj}{A chat object created from `create_chat()`} +\item{chat}{A chat object of class `tidychat`.} \item{message}{A character vector with one element. The message to add to the chat.} @@ -19,6 +22,11 @@ A chat object with the messages added \description{ Add messages to a chat object. } +\section{Methods (by class)}{ +\itemize{ +\item \code{add_message(tidychat)}: Add a message to a `tidychat` object. + +}} \examples{ \dontrun{dotenv::load_dot_env() chat_openai <- create_chat('openai', Sys.getenv('OAI_DEV_KEY'))|> diff --git a/man/add_model.Rd b/man/add_model.Rd index 2d43243..541ed82 100644 --- a/man/add_model.Rd +++ b/man/add_model.Rd @@ -4,12 +4,15 @@ \alias{add_model} \title{Add a model to a chat object.} \usage{ -add_model(chat_obj, model) +add_model(chat, model) } \arguments{ -\item{chat_obj}{A chat object created from `create_chat()`} +\item{chat}{An object of class `tidychat`.} -\item{model}{A character vector with one element. The model to use for the chat. You can use any chat completion model from openAI and mistral.ai here. Refer to their API docs for specific names.} +\item{model}{A character vector with one element. +The model to use for the chat. +You can use any chat completion model from openAI and mistral.ai here. +Refer to their API docs for specific names.} } \value{ A chat object with the model added diff --git a/man/add_params.Rd b/man/add_params.Rd index 127ac47..62e1e08 100644 --- a/man/add_params.Rd +++ b/man/add_params.Rd @@ -2,14 +2,17 @@ % Please edit documentation in R/add_params.R \name{add_params} \alias{add_params} +\alias{add_params.tidychat} \title{Add a model to a chat object.} \usage{ -add_params(chat_obj, ...) +add_params(chat, ...) + +\method{add_params}{tidychat}(chat, ...) } \arguments{ -\item{chat_obj}{A chat object created from `create_chat()`} - \item{...}{A named list of parameters to add to the chat object. Must be valid parameters from the API documentation.} + +\item{chat_obj}{A chat object created from `create_chat()`} } \value{ A chat object with the parameters added @@ -17,6 +20,11 @@ A chat object with the parameters added \description{ Add a model to a chat object. } +\section{Methods (by class)}{ +\itemize{ +\item \code{add_params(tidychat)}: Add parameters to a `tidychat` object. + +}} \examples{ \dontrun{dotenv::load_dot_env() chat_openai <- create_chat('openai', Sys.getenv('OAI_DEV_KEY'))|> diff --git a/man/append_message.Rd b/man/append_message.Rd new file mode 100644 index 0000000..68c33bb --- /dev/null +++ b/man/append_message.Rd @@ -0,0 +1,36 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/add_message.R +\name{append_message} +\alias{append_message} +\alias{append_message.tidychat} +\alias{append_message.ollama} +\alias{append_message.antropic} +\title{Append a message to a `tidychat` object.} +\usage{ +append_message(chat, response, ...) + +\method{append_message}{tidychat}(chat, response, ...) + +\method{append_message}{ollama}(chat, response, ...) + +\method{append_message}{antropic}(chat, response, ...) +} +\arguments{ +\item{chat}{An object of class `tidychat`.} + +\item{response}{The response as returned by `perform_query`.} + +\item{...}{Ignored for future compatibility.} +} +\description{ +Append a message to a `tidychat` object. +} +\section{Methods (by class)}{ +\itemize{ +\item \code{append_message(tidychat)}: Appends a message to a `tidychat` object. + +\item \code{append_message(ollama)}: Appends a message to a `ollama` object. + +\item \code{append_message(antropic)}: Appends a message to a `anthropic` object. + +}} diff --git a/man/create_chat.Rd b/man/create_chat.Rd index 9e0c594..190b644 100644 --- a/man/create_chat.Rd +++ b/man/create_chat.Rd @@ -2,23 +2,45 @@ % Please edit documentation in R/create_chat.R \name{create_chat} \alias{create_chat} +\alias{new_chat} +\alias{new_chat_openai} +\alias{new_chat_mistral} +\alias{new_chat_ollama} +\alias{new_chat_anthropic} \title{Create a chat object.} \usage{ create_chat( - vendor, + vendor = c("openai", "mistral", "ollama", "anthropic"), api_key = "", port = if (vendor == "ollama") 11434 else NULL, api_version = "" ) + +new_chat(engine, object, ...) + +new_chat_openai(key = Sys.getenv("OPENAI_API_KEY")) + +new_chat_mistral(key = Sys.getenv("MISTRAL_API_KEY")) + +new_chat_ollama(port) + +new_chat_anthropic(key, version) } \arguments{ -\item{vendor}{A character vector with one element. Currently, only 'openai', 'mistral', 'anthropic' and 'ollama' are supported.} +\item{vendor, engine}{A character vector with one element. +Currently, only "openai", "mistral", "anthropic" and "ollama" are supported.} + +\item{api_key, key}{The API key for the vendor"s chat engine. +If the vendor is "ollama", this parameter is not required.} + +\item{port}{The port number for the ollama chat engine. +Default to ollama"s standard port. If the vendor is not "ollama", this parameter is not required.} -\item{api_key}{The API key for the vendor's chat engine. If the vendor is 'ollama', this parameter is not required.} +\item{api_version, version}{Api version that is required for Anthropic} -\item{port}{The port number for the ollama chat engine. Default to ollama's standard port. If the vendor is not 'ollama', this parameter is not required.} +\item{object}{New query object from [httr2::request].} -\item{api_version}{Api version that is required for Anthropic} +\item{...}{Additional parameters to be passed to the chat engine query.} } \value{ A chat object @@ -29,7 +51,7 @@ Create a chat object. \examples{ \dontrun{ dotenv::load_dot_env() -chat_openai <- create_chat('openai', Sys.getenv('OAI_DEV_KEY')) -chat_mistral <- create_chat('mistral', Sys.getenv('MISTRAL_DEV_KEY')) +chat_openai <- create_chat("openai", Sys.getenv("OAI_DEV_KEY")) +chat_mistral <- create_chat("mistral", Sys.getenv("MISTRAL_DEV_KEY")) } } diff --git a/man/extract_chat.Rd b/man/extract_chat.Rd index 39c6bc2..2e0623c 100644 --- a/man/extract_chat.Rd +++ b/man/extract_chat.Rd @@ -2,12 +2,15 @@ % Please edit documentation in R/extract_chat.R \name{extract_chat} \alias{extract_chat} +\alias{extract_chat.tidychat} \title{Prints all messages to the console and saves them invisibly} \usage{ -extract_chat(chat_obj, silent = FALSE) +extract_chat(chat, silent = FALSE) + +\method{extract_chat}{tidychat}(chat, silent = FALSE) } \arguments{ -\item{chat_obj}{A chat object created from `create_chat()`} +\item{chat}{A chat object created from `create_chat()`} \item{silent}{A logical vector with one element. If TRUE, the messages are not printed to the console.} } @@ -17,6 +20,11 @@ A chat object with the responses added \description{ Prints all messages to the console and saves them invisibly } +\section{Methods (by class)}{ +\itemize{ +\item \code{extract_chat(tidychat)}: Extracts the chat messages from a `tidychat` object. + +}} \examples{ \dontrun{dotenv::load_dot_env() chat_openai <- create_chat('openai', Sys.getenv('OAI_DEV_KEY'))|> diff --git a/man/get_engine.Rd b/man/get_engine.Rd new file mode 100644 index 0000000..149a121 --- /dev/null +++ b/man/get_engine.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{get_engine} +\alias{get_engine} +\title{Get engine from a `tidychat` object} +\usage{ +get_engine(chat) +} +\arguments{ +\item{chat}{An object of class `tidychat`.} +} +\description{ +Get engine from a `tidychat` object +} diff --git a/man/get_messages.Rd b/man/get_messages.Rd new file mode 100644 index 0000000..46d18a5 --- /dev/null +++ b/man/get_messages.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/add_message.R +\name{get_messages} +\alias{get_messages} +\alias{get_messages.tidychat} +\title{Get messages from a chat object.} +\usage{ +get_messages(chat) + +\method{get_messages}{tidychat}(chat) +} +\arguments{ +\item{chat}{An object of class `tidychat`.} +} +\description{ +Get messages from a chat object. +} +\section{Methods (by class)}{ +\itemize{ +\item \code{get_messages(tidychat)}: Get messages from a `tidychat` object. + +}} diff --git a/man/get_model.Rd b/man/get_model.Rd new file mode 100644 index 0000000..5b0f16b --- /dev/null +++ b/man/get_model.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/add_model.R +\name{get_model} +\alias{get_model} +\alias{get_model.tidychat} +\title{Get model from a chat object.} +\usage{ +get_model(chat) + +\method{get_model}{tidychat}(chat) +} +\arguments{ +\item{chat}{An object of class `tidychat`.} +} +\description{ +Get model from a chat object. +} +\section{Methods (by class)}{ +\itemize{ +\item \code{get_model(tidychat)}: Gets a model from a `tidychat` object. + +}} diff --git a/man/get_params.Rd b/man/get_params.Rd new file mode 100644 index 0000000..dcac450 --- /dev/null +++ b/man/get_params.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/add_params.R, R/utils.R +\name{get_params} +\alias{get_params} +\alias{get_params.tidychat} +\title{Get parameters from a chat object.} +\usage{ +get_params(chat) + +\method{get_params}{tidychat}(chat) + +get_params(chat) +} +\arguments{ +\item{chat}{An object of class `tidychat`.} +} +\description{ +Get parameters from a chat object. + +Get params from a chat object +} +\section{Methods (by class)}{ +\itemize{ +\item \code{get_params(tidychat)}: Add parameters to a `tidychat` object. + +}} diff --git a/man/get_uses.Rd b/man/get_uses.Rd new file mode 100644 index 0000000..2cc09be --- /dev/null +++ b/man/get_uses.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{get_uses} +\alias{get_uses} +\title{Get uses from a chat object} +\usage{ +get_uses(chat) +} +\arguments{ +\item{chat}{An object of class `tidychat`.} +} +\description{ +Get uses from a chat object +} diff --git a/man/inc_uses.Rd b/man/inc_uses.Rd new file mode 100644 index 0000000..8a611c8 --- /dev/null +++ b/man/inc_uses.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{inc_uses} +\alias{inc_uses} +\title{Increment uses from a chat object by 1} +\usage{ +inc_uses(chat) +} +\arguments{ +\item{chat}{An object of class `tidychat`.} +} +\description{ +Increment uses from a chat object by 1 +} diff --git a/man/perform_chat.Rd b/man/perform_chat.Rd index 5bd0b17..ab13407 100644 --- a/man/perform_chat.Rd +++ b/man/perform_chat.Rd @@ -2,14 +2,20 @@ % Please edit documentation in R/perform_chat.R \name{perform_chat} \alias{perform_chat} +\alias{perform_chat.tidychat} \title{Sends the chat to the engine and adds the response to the chat object} \usage{ -perform_chat(chat_obj, dry_run = FALSE) +perform_chat(chat, dry_run = FALSE, ...) + +\method{perform_chat}{tidychat}(chat, dry_run = FALSE, ...) } \arguments{ -\item{chat_obj}{A chat object created from `create_chat()`} +\item{chat}{An object of class `tidychat`.} + +\item{dry_run}{A logical indicating whether to return the prepared_engine +`httr2` request without actually sending out the request to the vendor. Defaults to FALSE.} -\item{dry_run}{A logical indicating whether to return the prepared `httr2` request without actually sending out the request to the vendor. Defaults to FALSE.} +\item{...}{Ignored for future compatibility.} } \value{ A chat object with the responses added @@ -17,6 +23,11 @@ A chat object with the responses added \description{ Sends the chat to the engine and adds the response to the chat object } +\section{Methods (by class)}{ +\itemize{ +\item \code{perform_chat(tidychat)}: Perform a chat on a `tidychat` object. + +}} \examples{ \dontrun{dotenv::load_dot_env() chat_openai <- create_chat('openai', Sys.getenv('OAI_DEV_KEY'))|> @@ -45,4 +56,5 @@ chat_mistral <- create_chat('mistral', Sys.getenv('MISTRAL_DEV_KEY')) |> chat_mistral <- chat_mistral |> perform_chat() } + } diff --git a/man/perform_query.Rd b/man/perform_query.Rd new file mode 100644 index 0000000..56e1429 --- /dev/null +++ b/man/perform_query.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/perform_chat.R +\name{perform_query} +\alias{perform_query} +\alias{perform_query.tidychat} +\title{Perform a tidychat engine query} +\usage{ +perform_query(chat, prepared_query, ...) + +\method{perform_query}{tidychat}(chat, prepared_query, ...) +} +\arguments{ +\item{chat}{An object of class `tidychat`.} + +\item{prepared_query}{A prepared query as returned by `prepare_engine`.} + +\item{...}{Ignored for future compatibility.} +} +\description{ +Perform a tidychat engine query +} +\section{Methods (by class)}{ +\itemize{ +\item \code{perform_query(tidychat)}: Perform a tidychat engine query. + +}} diff --git a/man/prepare_engine.Rd b/man/prepare_engine.Rd new file mode 100644 index 0000000..e87bb26 --- /dev/null +++ b/man/prepare_engine.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/perform_chat.R +\name{prepare_engine} +\alias{prepare_engine} +\alias{prepare_engine.tidychat} +\alias{prepare_engine.anthropic} +\title{Prepare tidychat engine query} +\usage{ +prepare_engine(chat, ...) + +\method{prepare_engine}{tidychat}(chat, ...) + +\method{prepare_engine}{anthropic}(chat, ...) +} +\arguments{ +\item{chat}{An object of class `tidychat`.} + +\item{...}{Ignored for future compatibility.} +} +\description{ +Prepare tidychat engine query +} +\section{Methods (by class)}{ +\itemize{ +\item \code{prepare_engine(tidychat)}: Prepare a tidychat engine query. + +\item \code{prepare_engine(anthropic)}: Prepare an anthropic engine query. + +}} diff --git a/man/set_uses.Rd b/man/set_uses.Rd new file mode 100644 index 0000000..0c09ca5 --- /dev/null +++ b/man/set_uses.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{set_uses} +\alias{set_uses} +\title{Set uses from a chat object} +\usage{ +set_uses(chat, value) +} +\arguments{ +\item{chat}{An object of class `tidychat`.} + +\item{value}{An integer to set the uses to.} +} +\description{ +Set uses from a chat object +} diff --git a/tests/testthat/test-add_message.R b/tests/testthat/test-add_message.R index 4f7ecbf..480572f 100644 --- a/tests/testthat/test-add_message.R +++ b/tests/testthat/test-add_message.R @@ -1,37 +1,37 @@ test_that("user messages can be added to chat", { #ollama - chat <- create_chat('ollama', port = 14252) |> - add_message('This is a message') |> - add_message('And this is another one') + chat <- new_chat_ollama(port = 14252) |> + add_message("This is a message") |> + add_message("And this is another one") expect_equal( - chat$messages, + get_messages(chat), list( - list(role = 'user', content = 'This is a message'), - list(role = 'user', content = 'And this is another one') + list(role = "user", content = "This is a message"), + list(role = "user", content = "And this is another one") ) ) #openai - chat <- create_chat('openai', api_key = 'secret') |> - add_message('This is a message') |> - add_message('And this is another one') + chat <- new_chat_openai(key = "secret") |> + add_message("This is a message") |> + add_message("And this is another one") expect_equal( - chat$messages, + get_messages(chat), list( - list(role = 'user', content = 'This is a message'), - list(role = 'user', content = 'And this is another one') + list(role = "user", content = "This is a message"), + list(role = "user", content = "And this is another one") ) ) #mistral - chat <- create_chat('mistral', api_key = 'secret') |> - add_message('This is a message') |> - add_message('And this is another one') + chat <- new_chat_mistral(key = "secret") |> + add_message("This is a message") |> + add_message("And this is another one") expect_equal( - chat$messages, + get_messages(chat), list( - list(role = 'user', content = 'This is a message'), - list(role = 'user', content = 'And this is another one') + list(role = "user", content = "This is a message"), + list(role = "user", content = "And this is another one") ) ) }) @@ -39,38 +39,38 @@ test_that("user messages can be added to chat", { test_that("system messages can be added to chat", { #ollama - chat <- create_chat('ollama', port = 14252) |> - add_message(role = 'system', 'This is a SYSTEM message') |> - add_message('And this is another one') + chat <- new_chat_ollama(port = 14252) |> + add_message(role = "system", "This is a SYSTEM message") |> + add_message("And this is another one") expect_equal( - chat$messages, + get_messages(chat), list( - list(role = 'system', content = 'This is a SYSTEM message'), - list(role = 'user', content = 'And this is another one') + list(role = "system", content = "This is a SYSTEM message"), + list(role = "user", content = "And this is another one") ) ) #openai - chat <- create_chat('openai', api_key = 'secret') |> - add_message(role = 'system', 'This is a SYSTEM message') |> - add_message('And this is another one') + chat <- new_chat_openai(key = "secret") |> + add_message(role = "system", "This is a SYSTEM message") |> + add_message("And this is another one") expect_equal( - chat$messages, + get_messages(chat), list( - list(role = 'system', content = 'This is a SYSTEM message'), - list(role = 'user', content = 'And this is another one') + list(role = "system", content = "This is a SYSTEM message"), + list(role = "user", content = "And this is another one") ) ) #mistral - chat <- create_chat('mistral', api_key = 'secret') |> - add_message(role = 'system', 'This is a SYSTEM message') |> - add_message('And this is another one') + chat <- new_chat_mistral(key = "secret") |> + add_message(role = "system", "This is a SYSTEM message") |> + add_message("And this is another one") expect_equal( - chat$messages, + get_messages(chat), list( - list(role = 'system', content = 'This is a SYSTEM message'), - list(role = 'user', content = 'And this is another one') + list(role = "system", content = "This is a SYSTEM message"), + list(role = "user", content = "And this is another one") ) ) }) diff --git a/tests/testthat/test-add_model.R b/tests/testthat/test-add_model.R index fea8a6d..c5345ae 100644 --- a/tests/testthat/test-add_model.R +++ b/tests/testthat/test-add_model.R @@ -1,16 +1,16 @@ test_that("model can be added to chat", { #ollama - chat <- create_chat('ollama', port = 14252) |> - add_model('COOL-MODEL-NAME') - expect_equal(chat$model,'COOL-MODEL-NAME') + chat <- new_chat_ollama(port = 14252) |> + add_model("COOL-MODEL-NAME") + expect_equal(get_model(chat), "COOL-MODEL-NAME") #openai - chat <- create_chat('openai', api_key = 'secret') |> - add_model('COOL-MODEL-NAME') - expect_equal(chat$model,'COOL-MODEL-NAME') + chat <- new_chat_openai(key = "secret") |> + add_model("COOL-MODEL-NAME") + expect_equal(get_model(chat), "COOL-MODEL-NAME") #mistral - chat <- create_chat('mistral', api_key = 'secret') |> - add_model('COOL-MODEL-NAME') - expect_equal(chat$model,'COOL-MODEL-NAME') + chat <- new_chat_mistral(key = "secret") |> + add_model("COOL-MODEL-NAME") + expect_equal(get_model(chat), "COOL-MODEL-NAME") }) diff --git a/tests/testthat/test-add_params.R b/tests/testthat/test-add_params.R index 82cbcb1..026f2c3 100644 --- a/tests/testthat/test-add_params.R +++ b/tests/testthat/test-add_params.R @@ -1,21 +1,21 @@ test_that("parameters can be added to chat", { #ollama - chat <- create_chat('ollama', port = 14252) |> + chat <- new_chat_ollama(port = 14252) |> add_params(parameterA = 2343, parameterB = 23421) expect_equal( - chat$params, + get_params(chat), list(stream = FALSE, parameterA = 2343, parameterB = 23421) # stream = FALSE is default for this vendor # this also checks that multile `add_params` calls are merged ) #openai - chat <- create_chat('openai', api_key = 'secret') |> + chat <- new_chat_openai(key = "secret") |> add_params(parameterA = 2343, parameterB = 23421) - expect_equal(chat$params, list(parameterA = 2343, parameterB = 23421)) + expect_equal(get_params(chat), list(parameterA = 2343, parameterB = 23421)) #mistral - chat <- create_chat('mistral', api_key = 'secret') |> + chat <- new_chat_mistral(key = "secret") |> add_params(parameterA = 2343, parameterB = 23421) - expect_equal(chat$params, list(parameterA = 2343, parameterB = 23421)) + expect_equal(get_params(chat), list(parameterA = 2343, parameterB = 23421)) }) diff --git a/tests/testthat/test-create_chat.R b/tests/testthat/test-create_chat.R index edb5e69..f9c5d19 100644 --- a/tests/testthat/test-create_chat.R +++ b/tests/testthat/test-create_chat.R @@ -1,82 +1,80 @@ test_that("ollama chat can be created", { - chat <- create_chat('ollama', port = 14252) + chat <- new_chat_ollama(port = 14252) expect_equal( - chat$engine$url, - 'http://localhost:14252/api/chat' + chat$url, + "http://localhost:14252/api/chat" ) - expect_equal(chat$params$stream,FALSE) - expect_equal(chat$messages, list()) - expect_equal(chat$engine$headers, list()) + expect_equal(get_params(chat)$stream, FALSE) + expect_equal(get_messages(chat), list()) + expect_equal(chat$headers, list()) }) test_that("openai chat can be created", { - chat <- create_chat('openai', api_key = 'secret') + chat <- new_chat_openai(key = "secret") expect_equal( - chat$engine$url, - 'https://api.openai.com/v1/chat/completions' + chat$url, + "https://api.openai.com/v1/chat/completions" ) - expect_equal(chat$params$stream, NULL) - expect_equal(chat$messages, list()) + expect_equal(get_params(chat)$stream, NULL) + expect_equal(get_messages(chat), list()) headers_list <- list( - 'Authorization' = 'Bearer secret', - 'Content-Type' = 'application/json' + "Authorization" = "Bearer secret", + "Content-Type" = "application/json" ) attributes(headers_list) <- list( - names = c('Authorization', 'Content-Type'), - 'redact' = character() + names = c("Authorization", "Content-Type"), + "redact" = character() ) expect_equal( - chat$engine$headers, + chat$headers, headers_list ) }) test_that("mistral chat can be created", { - chat <- create_chat('mistral', api_key = 'secret') + chat <- new_chat_mistral(key = "secret") expect_equal( - chat$engine$url, - 'https://api.mistral.ai/v1/chat/completions' + chat$url, + "https://api.mistral.ai/v1/chat/completions" ) - expect_equal(chat$params$stream, NULL) - expect_equal(chat$messages, list()) - headers_list <- list('Bearer secret','application/json', 'application/json') + expect_equal(get_params(chat)$stream, NULL) + expect_equal(get_messages(chat), list()) + headers_list <- list("Bearer secret", "application/json", "application/json") attributes(headers_list) <- list( - names = c('Authorization', 'Content-Type', 'Accept'), - 'redact' = character() + names = c("Authorization", "Content-Type", "Accept"), + "redact" = character() ) expect_equal( - chat$engine$headers, + chat$headers, headers_list ) }) test_that("anthropic chat can be created", { - chat <- create_chat('anthropic', api_key = 'secret', api_version = '2023-06-01') + chat <- new_chat_anthropic(key = "secret", version = "2023-06-01") expect_equal( - chat$engine$url, - 'https://api.anthropic.com/v1/messages' + chat$url, + "https://api.anthropic.com/v1/messages" ) - expect_equal(chat$params$stream, NULL) - expect_equal(chat$messages, list()) - headers_list <- list('secret','application/json', '2023-06-01') + expect_equal(get_params(chat)$stream, NULL) + expect_equal(get_messages(chat), list()) + headers_list <- list("secret", "application/json", "2023-06-01") attributes(headers_list) <- list( - names = c('x-api-key', 'Content-Type', 'anthropic-version'), - 'redact' = character() + names = c("x-api-key", "Content-Type", "anthropic-version"), + "redact" = character() ) expect_equal( - chat$engine$headers, + chat$headers, headers_list ) }) - - diff --git a/tests/testthat/test-extract_chat.R b/tests/testthat/test-extract_chat.R index 873a32a..f6ba31a 100644 --- a/tests/testthat/test-extract_chat.R +++ b/tests/testthat/test-extract_chat.R @@ -1,18 +1,18 @@ test_that("chats can be extracted", { - chat <- create_chat('ollama',) |> + chat <- new_chat_ollama(port = 3000) |> add_message( - role = 'system', - message = 'You are a chatbot that completes texts. + role = "system", + message = "You are a chatbot that completes texts. You do not return the full text. - Just what you think completes the text.' + Just what you think completes the text." ) |> add_message( - # default role = 'user' - '2 + 2 is 4, minus 1 that\'s 3, ' + # default role = "user" + "2 + 2 is 4, minus 1 that\"s 3, " ) |> add_message( - role = 'assistant', - 'This is a fake reply' + role = "assistant", + "This is a fake reply" ) msgs <- chat |> extract_chat(silent = TRUE) @@ -20,15 +20,12 @@ test_that("chats can be extracted", { expect_equal( msgs, tibble::tibble( - role = c('system', 'user', 'assistant'), + role = c("system", "user", "assistant"), message = c( - 'You are a chatbot that completes texts.\n You do not return the full text.\n Just what you think completes the text.', - '2 + 2 is 4, minus 1 that\'s 3, ', - 'This is a fake reply' + "You are a chatbot that completes texts.\n You do not return the full text.\n Just what you think completes the text.", + "2 + 2 is 4, minus 1 that\"s 3, ", + "This is a fake reply" ) ) ) }) - - - diff --git a/tests/testthat/test-perform_chat.R b/tests/testthat/test-perform_chat.R index c874bfc..835e436 100644 --- a/tests/testthat/test-perform_chat.R +++ b/tests/testthat/test-perform_chat.R @@ -1,83 +1,83 @@ test_that("Mistral request fulfills API structure", { - chat <- create_chat('mistral', api_key = 'secret') |> - add_model('mistral-large-latest') |> + chat <- new_chat_mistral(key = "secret") |> + add_model("mistral-large-latest") |> add_params(temperature = 0.5, max_tokens = 100) |> add_message( - role = 'system', - message = 'You are a chatbot that completes texts. + role = "system", + message = "You are a chatbot that completes texts. You do not return the full text. - Just what you think completes the text.' + Just what you think completes the text." ) |> add_message( - # default role = 'user' - '2 + 2 is 4, minus 1 that\'s 3, ' + # default role = "user" + "2 + 2 is 4, minus 1 that\"s 3, " ) |> perform_chat(dry_run = TRUE) expect_equal( chat$body$data$messages, list( - list(role = 'system', content = 'You are a chatbot that completes texts.\n You do not return the full text.\n Just what you think completes the text.'), - list(role = 'user', content = '2 + 2 is 4, minus 1 that\'s 3, ') + list(role = "system", content = "You are a chatbot that completes texts.\n You do not return the full text.\n Just what you think completes the text."), + list(role = "user", content = "2 + 2 is 4, minus 1 that\"s 3, ") ) ) - expect_equal(chat$url, 'https://api.mistral.ai/v1/chat/completions') - expect_equal(chat$body$data$model, 'mistral-large-latest') + expect_equal(chat$url, "https://api.mistral.ai/v1/chat/completions") + expect_equal(chat$body$data$model, "mistral-large-latest") expect_equal(chat$body$data$temperature, 0.5) expect_equal(chat$body$data$max_tokens, 100) - expect_equal(chat$headers$`Content-Type`, 'application/json') - expect_equal(chat$headers$Authorization, 'Bearer secret') - expect_equal(chat$headers$Accept, 'application/json') + expect_equal(chat$headers$`Content-Type`, "application/json") + expect_equal(chat$headers$Authorization, "Bearer secret") + expect_equal(chat$headers$Accept, "application/json") }) test_that("openAI request fulfills API structure", { - chat <- create_chat('openai', api_key = 'secret') |> - add_model('gpt-3.5-turbo') |> + chat <- new_chat_openai(key = "secret") |> + add_model("gpt-3.5-turbo") |> add_params(temperature = 0.5, max_tokens = 100) |> add_message( - role = 'system', - message = 'You are a chatbot that completes texts. + role = "system", + message = "You are a chatbot that completes texts. You do not return the full text. - Just what you think completes the text.' + Just what you think completes the text." ) |> add_message( - # default role = 'user' - '2 + 2 is 4, minus 1 that\'s 3, ' + # default role = "user" + "2 + 2 is 4, minus 1 that\"s 3, " ) |> perform_chat(dry_run = TRUE) expect_equal( chat$body$data$messages, list( - list(role = 'system', content = 'You are a chatbot that completes texts.\n You do not return the full text.\n Just what you think completes the text.'), - list(role = 'user', content = '2 + 2 is 4, minus 1 that\'s 3, ') + list(role = "system", content = "You are a chatbot that completes texts.\n You do not return the full text.\n Just what you think completes the text."), + list(role = "user", content = "2 + 2 is 4, minus 1 that\"s 3, ") ) ) - expect_equal(chat$url, 'https://api.openai.com/v1/chat/completions') - expect_equal(chat$body$data$model, 'gpt-3.5-turbo') + expect_equal(chat$url, "https://api.openai.com/v1/chat/completions") + expect_equal(chat$body$data$model, "gpt-3.5-turbo") expect_equal(chat$body$data$temperature, 0.5) expect_equal(chat$body$data$max_tokens, 100) - expect_equal(chat$headers$`Content-Type`, 'application/json') - expect_equal(chat$headers$Authorization, 'Bearer secret') + expect_equal(chat$headers$`Content-Type`, "application/json") + expect_equal(chat$headers$Authorization, "Bearer secret") }) test_that("ollama request fulfills API structure", { - chat <- create_chat('ollama', api_key = 'secret', port = 25223) |> - add_model('gemma:7b') |> - add_message('What is love? IN 10 WORDS.') |> + chat <- new_chat_ollama(port = 25223) |> + add_model("gemma:7b") |> + add_message("What is love? IN 10 WORDS.") |> perform_chat(dry_run = TRUE) expect_equal( chat$body$data$messages, list( - list(role = 'user', content = 'What is love? IN 10 WORDS.') + list(role = "user", content = "What is love? IN 10 WORDS.") ) ) - expect_equal(chat$url, 'http://localhost:25223/api/chat') - expect_equal(chat$body$data$model, 'gemma:7b') + expect_equal(chat$url, "http://localhost:25223/api/chat") + expect_equal(chat$body$data$model, "gemma:7b") expect_equal(chat$body$data$stream, FALSE) expect_equal(chat$headers, list()) @@ -86,69 +86,64 @@ test_that("ollama request fulfills API structure", { test_that("Anthropic request fulfills API structure (when system msg present)", { - chat <- create_chat('anthropic', api_key = 'secret', api_version = '2023-06-01') |> - add_model('claude-3-opus-20240229') |> + chat <- new_chat_anthropic(key = "secret", version = "2023-06-01") |> + add_model("claude-3-opus-20240229") |> add_params(temperature = 0.5, max_tokens = 100) |> add_message( - role = 'system', - message = 'You are a chatbot that completes texts. + role = "system", + message = "You are a chatbot that completes texts. You do not return the full text. - Just what you think completes the text.' + Just what you think completes the text." ) |> add_message( - # default role = 'user' - '2 + 2 is 4, minus 1 that\'s 3, ' + # default role = "user" + "2 + 2 is 4, minus 1 that\"s 3, " ) |> perform_chat(dry_run = TRUE) expect_equal( chat$body$data$messages, list( - list(role = 'user', content = '2 + 2 is 4, minus 1 that\'s 3, ') + list(role = "user", content = "2 + 2 is 4, minus 1 that\"s 3, ") ) ) expect_equal( chat$body$data$system, - 'You are a chatbot that completes texts.\n You do not return the full text.\n Just what you think completes the text.' + "You are a chatbot that completes texts.\n You do not return the full text.\n Just what you think completes the text." ) - expect_equal(chat$url, 'https://api.anthropic.com/v1/messages') - expect_equal(chat$body$data$model, 'claude-3-opus-20240229') + expect_equal(chat$url, "https://api.anthropic.com/v1/messages") + expect_equal(chat$body$data$model, "claude-3-opus-20240229") expect_equal(chat$body$data$temperature, 0.5) expect_equal(chat$body$data$max_tokens, 100) - expect_equal(chat$headers$`Content-Type`, 'application/json') - expect_equal(chat$headers$`x-api-key`, 'secret') - expect_equal(chat$headers$`anthropic-version`, '2023-06-01') + expect_equal(chat$headers$`Content-Type`, "application/json") + expect_equal(chat$headers$`x-api-key`, "secret") + expect_equal(chat$headers$`anthropic-version`, "2023-06-01") }) test_that("Anthropic request fulfills API structure (without system msg)", { - chat <- create_chat('anthropic', api_key = 'secret', api_version = '2023-06-01') |> - add_model('claude-3-opus-20240229') |> + chat <- new_chat_anthropic(key = "secret", version = "2023-06-01") |> + add_model("claude-3-opus-20240229") |> add_params(temperature = 0.5, max_tokens = 100) |> add_message( - # default role = 'user' - '2 + 2 is 4, minus 1 that\'s 3, ' + # default role = "user" + "2 + 2 is 4, minus 1 that\"s 3, " ) |> perform_chat(dry_run = TRUE) expect_equal( chat$body$data$messages, list( - list(role = 'user', content = '2 + 2 is 4, minus 1 that\'s 3, ') + list(role = "user", content = "2 + 2 is 4, minus 1 that\"s 3, ") ) ) expect_equal(chat$body$data$system, NULL) - expect_equal(chat$url, 'https://api.anthropic.com/v1/messages') - expect_equal(chat$body$data$model, 'claude-3-opus-20240229') + expect_equal(chat$url, "https://api.anthropic.com/v1/messages") + expect_equal(chat$body$data$model, "claude-3-opus-20240229") expect_equal(chat$body$data$temperature, 0.5) expect_equal(chat$body$data$max_tokens, 100) - expect_equal(chat$headers$`Content-Type`, 'application/json') - expect_equal(chat$headers$`x-api-key`, 'secret') - expect_equal(chat$headers$`anthropic-version`, '2023-06-01') - + expect_equal(chat$headers$`Content-Type`, "application/json") + expect_equal(chat$headers$`x-api-key`, "secret") + expect_equal(chat$headers$`anthropic-version`, "2023-06-01") }) - - - -