Skip to content

Commit

Permalink
make pal() developer-facing (closes #28)
Browse files Browse the repository at this point in the history
`pal()` -> `.pal_init()`
`.pal()` -> `.pal_addin()`
  • Loading branch information
simonpcouch committed Oct 11, 2024
1 parent e1cfe6b commit 0e7e0dd
Show file tree
Hide file tree
Showing 22 changed files with 116 additions and 73 deletions.
3 changes: 2 additions & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
2 changes: 1 addition & 1 deletion R/pal-add-remove.R
Original file line number Diff line number Diff line change
Expand Up @@ -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()].
Expand Down
18 changes: 17 additions & 1 deletion R/gadget.R → R/pal-addin.R
Original file line number Diff line number Diff line change
@@ -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()),
Expand Down
18 changes: 8 additions & 10 deletions R/pal.R → R/pal-init.R
Original file line number Diff line number Diff line change
@@ -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 ")`,
Expand All @@ -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"
#' )
Expand All @@ -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"),
...,
Expand Down
8 changes: 4 additions & 4 deletions R/rstudioapi.R
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
)
Expand Down Expand Up @@ -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)
}
)
Expand Down
3 changes: 2 additions & 1 deletion README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- TODO: link to getting started instead of `.?pal_init()` -->

## Example

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 16 additions & 10 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion inst/rstudio/addins.dcf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Name: Pal
Description: LLM assistants for R
Binding: .pal
Binding: .pal_addin
Interactive: false
2 changes: 1 addition & 1 deletion man-roxygen/manual-interface.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#' pal directly, use:
#'
#' ```r
#' pal_<%= role %> <- pal("<%= role %>")
#' pal_<%= role %> <- .pal_init("<%= role %>")
#' ```
#'
#' Then, to submit a query, run:
Expand Down
21 changes: 21 additions & 0 deletions man/dot-pal_addin.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 13 additions & 14 deletions man/pal.Rd → man/dot-pal_init.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/pal-package.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/pal_add_remove.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/pal_cli.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/pal_roxygen.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/pal_testthat.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 10 additions & 10 deletions tests/testthat/_snaps/pal.md → tests/testthat/_snaps/pal-init.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,57 @@
# initializing a pal

Code
pal("cli")
.pal_init("cli")
Message
-- A cli pal using claude-3-5-sonnet-20240620.

---

Code
pal("testthat")
.pal_init("testthat")
Message
-- A testthat pal using claude-3-5-sonnet-20240620.

# 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.

---

Code
pal("cli")
.pal_init("cli")
Message
-- A cli pal using claude-3-5-sonnet-20240620.

# 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()`.

2 changes: 1 addition & 1 deletion tests/testthat/test-pal-add-remove.R
Original file line number Diff line number Diff line change
Expand Up @@ -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()))

Expand Down
8 changes: 4 additions & 4 deletions tests/testthat/test-pal-class.R
Original file line number Diff line number Diff line change
Expand Up @@ -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")
})
Expand All @@ -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())
})

Expand All @@ -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))
Expand Down
Loading

0 comments on commit 0e7e0dd

Please sign in to comment.