diff --git a/NAMESPACE b/NAMESPACE index b64daef..8f2065a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,7 +1,8 @@ # Generated by roxygen2: do not edit by hand S3method(print,pal_response) -export(pal) +export(.pal_addin) +export(.pal_init) export(pal_add) import(rlang) importFrom(elmer,content_image_file) diff --git a/R/pal-add-remove.R b/R/pal-add-remove.R index 13e9278..aceb0e1 100644 --- a/R/pal-add-remove.R +++ b/R/pal-add-remove.R @@ -5,7 +5,7 @@ #' the function a role and prompt, the pal will be available on the command #' palette. #' -#' @param role A single string giving the [pal()] role. +#' @param role A single string giving the [.pal_init()] role. # TODO: actually do this once elmer implements #' @param prompt A file path to a markdown file giving the system prompt or #' the output of [elmer::interpolate()]. diff --git a/R/gadget.R b/R/pal-addin.R similarity index 75% rename from R/gadget.R rename to R/pal-addin.R index c28b34f..070c733 100644 --- a/R/gadget.R +++ b/R/pal-addin.R @@ -1,4 +1,20 @@ -.pal <- function() { +#' Run the pal addin +#' +#' @description +#' The pal addin allows users to interactively select a pal to +#' interface with the current selection. **This function is not +#' intended to be interfaced with in regular usage of the package.** +#' To launch the pal addin in RStudio, navigate to `Addins > Pal` +#' and/or register the addin with a shortcut via +#' `Tools > Modify Keyboard Shortcuts > Search "Pal"`—we suggest `Ctrl+Cmd+P` +#' (or `Ctrl+Alt+P` on non-macOS). +#' +#' @returns +#' `NULL`, invisibly. Called for the side effect of launching the pal addin +#' and interfacing with selected text. +#' +#' @export +.pal_addin <- function() { # suppress "Listening on..." message and rethrow errors with new context try_fetch( suppressMessages(pal_fn <- .pal_app()), diff --git a/R/pal.R b/R/pal-init.R similarity index 80% rename from R/pal.R rename to R/pal-init.R index 0409e71..55e80b5 100644 --- a/R/pal.R +++ b/R/pal-init.R @@ -1,13 +1,11 @@ -#' Create a pal +#' Initialize a pal #' #' @description -#' Pals are persistent, ergonomic LLM assistants designed to help you complete -#' repetitive, hard-to-automate tasks quickly. After selecting some code, -#' press the keyboard shortcut you've chosen to trigger the pal addin (we -#' suggest `Ctrl+Cmd+P`), select the pal, and watch your code be rewritten. +#' **Users typically should not need to call this function.** #' -#' **Users typically should not need to call this function.** The pal -#' addin will create needed pals on-the-fly. +#' * Create new pals that will automatically be registered with this function +#' with [pal_add()]. +#' * The [pal addin][.pal_addin()] will initialize needed pals on-the-fly. #' #' @param role The identifier for a pal prompt. By default one #' of `r glue::glue_collapse(paste0("[", glue::double_quote(default_roles), "]", "[pal_", default_roles, "]"), ", ", last = " or ")`, @@ -28,10 +26,10 @@ #' #' @examplesIf FALSE #' # to create a chat with claude: -#' pal() +#' .pal_init() #' #' # or with OpenAI's 4o-mini: -#' pal( +#' .pal_init( #' "chat_openai", #' model = "gpt-4o-mini" #' ) @@ -44,7 +42,7 @@ #' .pal_args = list(model = "gpt-4o-mini") #' ) #' @export -pal <- function( +.pal_init <- function( role = NULL, fn = getOption(".pal_fn", default = "chat_claude"), ..., diff --git a/R/rstudioapi.R b/R/rstudioapi.R index 93c6662..2cbe337 100644 --- a/R/rstudioapi.R +++ b/R/rstudioapi.R @@ -5,9 +5,9 @@ rs_replace_selection <- function(context, role) { pal <- get(paste0(".pal_last_", role)) } else { tryCatch( - pal <- pal(role), + pal <- .pal_init(role), error = function(e) { - rstudioapi::showDialog("Error", "Unable to create a pal. See `?pal()`.") + rstudioapi::showDialog("Error", "Unable to create a pal. See `?.pal_init()`.") return(NULL) } ) @@ -118,9 +118,9 @@ rs_prefix_selection <- function(context, role) { pal <- get(paste0(".pal_last_", role)) } else { tryCatch( - pal <- pal(role), + pal <- .pal_init(role), error = function(e) { - rstudioapi::showDialog("Error", "Unable to create a pal. See `?pal()`.") + rstudioapi::showDialog("Error", "Unable to create a pal. See `?.pal_init()`.") return(NULL) } ) diff --git a/README.Rmd b/README.Rmd index 7ad0a3e..7ffe1fe 100644 --- a/README.Rmd +++ b/README.Rmd @@ -31,8 +31,9 @@ You can install pal like so: pak::pak("simonpcouch/pal") ``` -Then, ensure that you have an [`ANTHROPIC_API_KEY`](https://console.anthropic.com/) set in your `.Renviron`---see [usethis::edit_r_environ()](https://usethis.r-lib.org/reference/edit.html) for more information. If you'd like to use an LLM other than Anthropic's Claude 3.5 Sonnet—like OpenAI's ChatGPT—to power the pal, see [`?pal()`](https://simonpcouch.github.io/pal/reference/pal.html) for information on how to set default metadata on that model. +Then, ensure that you have an [`ANTHROPIC_API_KEY`](https://console.anthropic.com/) set in your `.Renviron`---see [usethis::edit_r_environ()](https://usethis.r-lib.org/reference/edit.html) for more information. If you'd like to use an LLM other than Anthropic's Claude 3.5 Sonnet—like OpenAI's ChatGPT—to power the pal, see [`?.pal_init()`](https://simonpcouch.github.io/pal/reference/pal.html) for information on how to set default metadata on that model. + ## Example diff --git a/README.md b/README.md index e5ada50..17a18d5 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Then, ensure that you have an [usethis::edit_r_environ()](https://usethis.r-lib.org/reference/edit.html) for more information. If you’d like to use an LLM other than Anthropic’s Claude 3.5 Sonnet—like OpenAI’s ChatGPT—to power the pal, see -[`?pal()`](https://simonpcouch.github.io/pal/reference/pal.html) for +[`? .pal_init()`](https://simonpcouch.github.io/pal/reference/pal.html) for information on how to set default metadata on that model. ## Example diff --git a/_pkgdown.yml b/_pkgdown.yml index 6c0dc4a..5ac3ec9 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -3,13 +3,19 @@ template: bootstrap: 5 reference: -- title: Create pals - contents: - - pal - - pal_add_remove -- title: Pals for package development - contents: - - pal_roxygen - - pal_testthat - - pal_cli -# to come: custom pals + - title: "Using pal" + desc: > + Users of pal will generally not need to call any functions from the + package directly. See the "Getting Started" vignette to get + started and "Custom Pals" to learn how to add your own pals. +# TODO: add these vignettes + - title: Pals for package development + contents: + - pal_roxygen + - pal_testthat + - pal_cli + - title: Infrastructure + contents: + - pal_add_remove + - .pal_init + - .pal_addin diff --git a/inst/rstudio/addins.dcf b/inst/rstudio/addins.dcf index c8323a1..dfb2b0f 100644 --- a/inst/rstudio/addins.dcf +++ b/inst/rstudio/addins.dcf @@ -1,4 +1,4 @@ Name: Pal Description: LLM assistants for R -Binding: .pal +Binding: .pal_addin Interactive: false diff --git a/man-roxygen/manual-interface.R b/man-roxygen/manual-interface.R index b44e6a6..610aa03 100644 --- a/man-roxygen/manual-interface.R +++ b/man-roxygen/manual-interface.R @@ -4,7 +4,7 @@ #' pal directly, use: #' #' ```r -#' pal_<%= role %> <- pal("<%= role %>") +#' pal_<%= role %> <- .pal_init("<%= role %>") #' ``` #' #' Then, to submit a query, run: diff --git a/man/dot-pal_addin.Rd b/man/dot-pal_addin.Rd new file mode 100644 index 0000000..70fd74d --- /dev/null +++ b/man/dot-pal_addin.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/pal-addin.R +\name{.pal_addin} +\alias{.pal_addin} +\title{Run the pal addin} +\usage{ +.pal_addin() +} +\value{ +\code{NULL}, invisibly. Called for the side effect of launching the pal addin +and interfacing with selected text. +} +\description{ +The pal addin allows users to interactively select a pal to +interface with the current selection. \strong{This function is not +intended to be interfaced with in regular usage of the package.} +To launch the pal addin in RStudio, navigate to \code{Addins > Pal} +and/or register the addin with a shortcut via +\verb{Tools > Modify Keyboard Shortcuts > Search "Pal"}—we suggest \code{Ctrl+Cmd+P} +(or \code{Ctrl+Alt+P} on non-macOS). +} diff --git a/man/pal.Rd b/man/dot-pal_init.Rd similarity index 76% rename from man/pal.Rd rename to man/dot-pal_init.Rd index 0e41413..36b3607 100644 --- a/man/pal.Rd +++ b/man/dot-pal_init.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/pal.R -\name{pal} -\alias{pal} -\title{Create a pal} +% Please edit documentation in R/pal-init.R +\name{.pal_init} +\alias{.pal_init} +\title{Initialize a pal} \usage{ -pal( +.pal_init( role = NULL, fn = getOption(".pal_fn", default = "chat_claude"), ..., @@ -27,13 +27,12 @@ set the \code{.pal_args} option; see examples below.} \item{.ns}{The package that the \verb{new_*()} function is exported from.} } \description{ -Pals are persistent, ergonomic LLM assistants designed to help you complete -repetitive, hard-to-automate tasks quickly. After selecting some code, -press the keyboard shortcut you've chosen to trigger the pal addin (we -suggest \code{Ctrl+Cmd+P}), select the pal, and watch your code be rewritten. - -\strong{Users typically should not need to call this function.} The pal -addin will create needed pals on-the-fly. +\strong{Users typically should not need to call this function.} +\itemize{ +\item Create new pals that will automatically be registered with this function +with \code{\link[=pal_add]{pal_add()}}. +\item The \link[=.pal_addin]{pal addin} will initialize needed pals on-the-fly. +} } \details{ If you have an Anthropic API key (or another API key and the \verb{pal_*()} @@ -44,10 +43,10 @@ to look for your API credentials and will call needed functions by itself. \examples{ \dontshow{if (FALSE) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} # to create a chat with claude: -pal() + .pal_init() # or with OpenAI's 4o-mini: -pal( + .pal_init( "chat_openai", model = "gpt-4o-mini" ) diff --git a/man/pal-package.Rd b/man/pal-package.Rd index 2bb1e07..d21aa48 100644 --- a/man/pal-package.Rd +++ b/man/pal-package.Rd @@ -2,6 +2,7 @@ % Please edit documentation in R/pal-package.R \docType{package} \name{pal-package} +\alias{pal} \alias{pal-package} \title{pal: LLM assistants for R} \description{ diff --git a/man/pal_add_remove.Rd b/man/pal_add_remove.Rd index dda56ee..918265a 100644 --- a/man/pal_add_remove.Rd +++ b/man/pal_add_remove.Rd @@ -11,7 +11,7 @@ pal_add(role, prompt = NULL, interface = c("replace", "prefix", "suffix")) pal_remove(role) } \arguments{ -\item{role}{A single string giving the \code{\link[=pal]{pal()}} role.} +\item{role}{A single string giving the \code{\link[= .pal_init]{ .pal_init()}} role.} \item{prompt}{A file path to a markdown file giving the system prompt or the output of \code{\link[elmer:interpolate]{elmer::interpolate()}}.} diff --git a/man/pal_cli.Rd b/man/pal_cli.Rd index 4b83a64..be801a2 100644 --- a/man/pal_cli.Rd +++ b/man/pal_cli.Rd @@ -175,7 +175,7 @@ Returns: Pals are typically interfaced with via the pal addin. To call the cli pal directly, use: -\if{html}{\out{
}}\preformatted{pal_cli <- pal("cli") +\if{html}{\out{
}}\preformatted{pal_cli <- .pal_init("cli") }\if{html}{\out{
}} Then, to submit a query, run: diff --git a/man/pal_roxygen.Rd b/man/pal_roxygen.Rd index 0f6c64e..68f376e 100644 --- a/man/pal_roxygen.Rd +++ b/man/pal_roxygen.Rd @@ -150,7 +150,7 @@ Returns: Pals are typically interfaced with via the pal addin. To call the roxygen pal directly, use: -\if{html}{\out{
}}\preformatted{pal_roxygen <- pal("roxygen") +\if{html}{\out{
}}\preformatted{pal_roxygen <- .pal_init("roxygen") }\if{html}{\out{
}} Then, to submit a query, run: diff --git a/man/pal_testthat.Rd b/man/pal_testthat.Rd index 60e7b01..9cd39c7 100644 --- a/man/pal_testthat.Rd +++ b/man/pal_testthat.Rd @@ -168,7 +168,7 @@ Returns: Pals are typically interfaced with via the pal addin. To call the testthat pal directly, use: -\if{html}{\out{
}}\preformatted{pal_testthat <- pal("testthat") +\if{html}{\out{
}}\preformatted{pal_testthat <- .pal_init("testthat") }\if{html}{\out{
}} Then, to submit a query, run: diff --git a/tests/testthat/_snaps/pal.md b/tests/testthat/_snaps/pal-init.md similarity index 69% rename from tests/testthat/_snaps/pal.md rename to tests/testthat/_snaps/pal-init.md index 4f8276a..5d751ef 100644 --- a/tests/testthat/_snaps/pal.md +++ b/tests/testthat/_snaps/pal-init.md @@ -1,7 +1,7 @@ # initializing a pal Code - pal("cli") + .pal_init("cli") Message -- A cli pal using claude-3-5-sonnet-20240620. @@ -9,7 +9,7 @@ --- Code - pal("testthat") + .pal_init("testthat") Message -- A testthat pal using claude-3-5-sonnet-20240620. @@ -17,7 +17,7 @@ # can use other models Code - pal("cli", fn = "chat_openai", model = "gpt-4o-mini") + .pal_init("cli", fn = "chat_openai", model = "gpt-4o-mini") Message -- A cli pal using gpt-4o-mini. @@ -25,7 +25,7 @@ --- Code - pal("cli") + .pal_init("cli") Message -- A cli pal using claude-3-5-sonnet-20240620. @@ -33,25 +33,25 @@ # errors informatively with bad role Code - pal() + .pal_init() Condition - Error in `pal()`: + Error in `.pal_init()`: ! `role` must be a single string, not `NULL`. --- Code - pal(NULL) + .pal_init(NULL) Condition - Error in `pal()`: + Error in `.pal_init()`: ! `role` must be a single string, not `NULL`. --- Code - pal("beep bop boop") + .pal_init("beep bop boop") Condition - Error in `pal()`: + Error in `.pal_init()`: ! No pals with role `beep bop boop` registered. i See `pal_add()`. diff --git a/tests/testthat/test-pal-add-remove.R b/tests/testthat/test-pal-add-remove.R index 365821e..aabcdb9 100644 --- a/tests/testthat/test-pal-add-remove.R +++ b/tests/testthat/test-pal-add-remove.R @@ -11,7 +11,7 @@ test_that("pal addition and removal works", { expect_true(".pal_prompt_boopery" %in% names(pal_env())) expect_true(".pal_rs_boopery" %in% names(pal_env())) - pal_boopery <- pal("boopery") + pal_boopery <- .pal_init("boopery") expect_snapshot(pal_boopery) expect_true(".pal_last_boopery" %in% names(pal_env())) diff --git a/tests/testthat/test-pal-class.R b/tests/testthat/test-pal-class.R index c8d46ca..81b1891 100644 --- a/tests/testthat/test-pal-class.R +++ b/tests/testthat/test-pal-class.R @@ -3,7 +3,7 @@ test_that("can find the previous pal", { skip_if_not_installed("withr") withr::local_options(.pal_fn = NULL, .pal_args = NULL) - cli_pal <- pal("cli") + cli_pal <- .pal_init("cli") expect_no_error(response <- cli_pal$chat(stop("Error message here"))) expect_s3_class(response, "pal_response") }) @@ -13,7 +13,7 @@ test_that("chat errors informatively with no input", { skip_if_not_installed("withr") withr::local_options(.pal_fn = NULL, .pal_args = NULL) - cli_pal <- pal("cli") + cli_pal <- .pal_init("cli") expect_snapshot(error = TRUE, cli_pal$chat()) }) @@ -22,12 +22,12 @@ test_that("pal_chat effectively integrates system prompt", { skip_if_not_installed("withr") withr::local_options(.pal_fn = NULL, .pal_args = NULL) - cli_pal <- pal("cli") + cli_pal <- .pal_init("cli") response <- cli_pal$chat(stop("Error message here")) expect_true(grepl("cli_abort", response)) expect_true(grepl("Error message here", response)) - testthat_pal <- pal("testthat") + testthat_pal <- .pal_init("testthat") response <- testthat_pal$chat(expect_error(beep_bop_boop())) expect_true(grepl("expect_snapshot", response)) expect_true(grepl("beep_bop_boop", response)) diff --git a/tests/testthat/test-pal.R b/tests/testthat/test-pal-init.R similarity index 63% rename from tests/testthat/test-pal.R rename to tests/testthat/test-pal-init.R index c36c7b0..ee71819 100644 --- a/tests/testthat/test-pal.R +++ b/tests/testthat/test-pal-init.R @@ -3,8 +3,8 @@ test_that("initializing a pal", { skip_if_not_installed("withr") withr::local_options(.pal_fn = NULL, .pal_args = NULL) - expect_snapshot(pal("cli")) - expect_snapshot(pal("testthat")) + expect_snapshot(.pal_init("cli")) + expect_snapshot(.pal_init("testthat")) }) test_that("can use other models", { @@ -13,18 +13,18 @@ test_that("can use other models", { withr::local_options(.pal_fn = NULL, .pal_args = NULL) # respects other argument values - expect_snapshot(pal("cli", fn = "chat_openai", model = "gpt-4o-mini")) + expect_snapshot(.pal_init("cli", fn = "chat_openai", model = "gpt-4o-mini")) # respects .clipal_* options withr::local_options( .clipal_fn = "chat_openai", .clipal_args = list(model = "gpt-4o-mini") ) - expect_snapshot(pal("cli")) + expect_snapshot(.pal_init("cli")) }) test_that("errors informatively with bad role", { - expect_snapshot(pal(), error = TRUE) - expect_snapshot(pal(NULL), error = TRUE) - expect_snapshot(pal("beep bop boop"), error = TRUE) + expect_snapshot(.pal_init(), error = TRUE) + expect_snapshot(.pal_init(NULL), error = TRUE) + expect_snapshot(.pal_init("beep bop boop"), error = TRUE) }) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index 2113d2b..234a043 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -4,10 +4,10 @@ test_that(".pal_last is up to date with most recent pal", { skip_if_not_installed("withr") withr::local_options(.pal_fn = NULL, .pal_args = NULL) - pal("cli") + .pal_init("cli") expect_snapshot(.pal_last) expect_snapshot(.pal_last_cli) - pal("cli", "chat_openai", model = "gpt-4o-mini") + .pal_init("cli", "chat_openai", model = "gpt-4o-mini") expect_snapshot(.pal_last) })