Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

transition to new ellmer config interface #81

Merged
merged 6 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: pal
Title: LLM assistants for R
Version: 0.0.3
Version: 0.0.4
Authors@R:
person("Simon", "Couch", , "[email protected]", role = c("aut", "cre"),
comment = c(ORCID = "0000-0001-5676-5107"))
Expand Down
19 changes: 19 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# pal (development version)

* Initial CRAN submission.

## Notable changes pre-CRAN submission

Early adopters of the package will note two changes made shortly before the
release of the package to CRAN:

* The configuration options `.pal_fn` and `.pal_args` have been
transitioned to one option, `.pal_chat`. That option takes an ellmer Chat, e.g.
`options(.pal_chat = ellmer::chat_claude())`.
If you've configured an ellmer model using the previous options, you'll get
an error that automatically translates to the new code you need to use.

* There is no longer a default ellmer model. Early in the development
of pal, if you had an `ANTHROPIC_API_KEY` set up, the addin would
"just work." While this was convenient for Claude users, but it means that the
package spends money on the users behalf without any explicit opt-in.
4 changes: 4 additions & 0 deletions R/init-addin.R
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
#'
#' @export
.init_addin <- function() {
if (is.null(fetch_pal_chat())) {
return(invisible())
}

# suppress "Listening on..." message and rethrow errors with new context
try_fetch(
suppressMessages(pal_fn_name <- .pal_app()),
Expand Down
40 changes: 10 additions & 30 deletions R/init-pal.R
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,28 @@
#' @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 ")`,
#' though custom pals can be added with [.pal_add()].
#' @param fn A `new_*()` function, likely from the ellmer package. Defaults
#' to [ellmer::chat_claude()]. To set a persistent alternative default,
#' set the `.pal_fn` option; see examples below.
#' @param .ns The package that the `new_*()` function is exported from.
#' @param ... Additional arguments to `fn`. The `system_prompt` argument will
#' be ignored if supplied. To set persistent defaults,
#' set the `.pal_args` option; see examples below.
#'
#' @details
#' If you have an Anthropic API key (or another API key and the `pal_*()`
#' options) set and this package installed, you are ready to using the addin
#' in any R session with no setup or library loading required; the addin knows
#' to look for your API credentials and will call needed functions by itself.
#' @param .pal_chat An ellmer Chat, e.g.
#' `function() ellmer::chat_claude()`. Defaults to the option by the same name,
#' so e.g. set `options(.pal_chat = ellmer::chat_claude()` in your
#' `.Rprofile` to configure pal with ellmer every time you start a new R session.
#'
#' @examplesIf FALSE
#' # to create a chat with claude:
#' .init_pal()
#'
#' # or with OpenAI's 4o-mini:
#' .init_pal(
#' "chat_openai",
#' model = "gpt-4o-mini"
#' )
#' .init_pal(.pal_chat = ellmer::chat_openai(model = "gpt-4o-mini"))
#'
#' # to set OpenAI's 4o-mini as the default, for example, set the
#' # following options (possibly in your .Rprofile, if you'd like
#' # to set OpenAI's 4o-mini as the default model powering pal, for example,
#' # set the following option (possibly in your .Rprofile, if you'd like
#' # them to persist across sessions):
#' options(
#' .pal_fn = "chat_openai",
#' .pal_args = list(model = "gpt-4o-mini")
#' .pal_chat = ellmer::chat_openai(model = "gpt-4o-mini")
#' )
#' @export
.init_pal <- function(
role = NULL,
fn = getOption(".pal_fn", default = "chat_claude"),
...,
.ns = "ellmer"
.pal_chat = getOption(".pal_chat")
) {
check_role(role, allow_default = TRUE)
if (!role %in% list_pals()) {
Expand All @@ -56,12 +41,7 @@
))
}

Pal$new(
role = role,
fn = fn,
...,
.ns = .ns
)
Pal$new(role = role, .pal_chat = .pal_chat)
}

default_roles <- c("cli", "testthat", "roxygen")
4 changes: 3 additions & 1 deletion R/options.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
#'
#' * `.pal_dir` is the directory where pal prompts live. See the pal [directory]
#' help-page for more information.
#' * `.pal_fn` and `.pal_args` determine the underlying LLM powering each pal.
#' * `.pal_chat` determines the underlying LLM powering each pal.
#' See the "Choosing a model" section of `vignette("pal", package = "pal")`
#' for more information.
#'
#' @name pal_options
#' @aliases .pal_dir
#' @aliases .pal_chat
NULL
59 changes: 54 additions & 5 deletions R/pal-class.R
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
Pal <- R6::R6Class(
"Pal",
public = list(
initialize = function(role, fn, ..., .ns) {
initialize = function(role, .pal_chat = getOption(".pal_chat")) {
self$role <- role
args <- list(...)
default_args <- getOption(".pal_args", default = list())
args <- modifyList(default_args, args)

Chat <- rlang::eval_bare(rlang::call2(fn, !!!args, .ns = .ns))
Chat <- .pal_chat$clone()

Chat$set_system_prompt(get(paste0(".pal_prompt_", role), envir = pal_env()))
private$Chat <- Chat

Expand Down Expand Up @@ -39,6 +37,57 @@ Pal <- R6::R6Class(
)
)

# this function fails with messages and a NULL return value rather than errors
# so that, when called from inside the addin, there's no dialog box raised by RStudio
fetch_pal_chat <- function(.pal_chat = getOption(".pal_chat")) {
# first, check for old options
.pal_fn <- getOption(".pal_fn")
.pal_args <- getOption(".pal_args")
if (!is.null(.pal_fn) && is.null(.pal_chat)) {
new_option <-
cli::format_inline("{deparse(rlang::call2(.pal_fn, !!!.pal_args))}")
cli::cli_inform(c(
"{.pkg pal} now uses the option {cli::col_blue('.pal_chat')} instead
of {cli::col_blue('.pal_fn')} and {cli::col_blue('.pal_args')}.",
"i" = "Set
{.code options(.pal_chat = {deparse(rlang::call2(.pal_fn, !!!.pal_args))})}
instead."
), call = NULL)
return(NULL)
}

if (is.null(.pal_chat)) {
cli::cli_inform(
c(
"!" = "pal requires configuring an ellmer Chat with the
{cli::col_blue('.pal_chat')} option.",
"i" = "Set e.g.
{.code {cli::col_green('options(.pal_chat = ellmer::chat_claude()')}}
in your {.file ~/.Rprofile} and restart R.",
"i" = "See \"Choosing a model\" in
{.code vignette(\"pal\", package = \"pal\")} to learn more."
),
call = NULL
)
return(NULL)
}

if (!inherits(.pal_chat, "Chat")) {
cli::cli_inform(
c(
"!" = "The option {cli::col_blue('.pal_chat')} must be an ellmer
Chat object, not {.obj_type_friendly { .pal_chat}}.",
"i" = "See \"Choosing a model\" in
{.code vignette(\"pal\", package = \"pal\")} to learn more."
),
call = NULL
)
return(NULL)
}

.pal_chat
}

#' @export
print.pal_response <- function(x, ...) {
cat(x)
Expand Down
8 changes: 5 additions & 3 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,17 @@ The pal package provides a library of ergonomic LLM assistants designed to help

## Installation

You can install pal like so:
Getting started with pal takes three steps.

**1)** Install the pal package like so:

``` r
pak::pak("simonpcouch/pal")
```

Then, ensure that you have an [`ANTHROPIC_API_KEY`](https://console.anthropic.com/) environment variable set, and you're ready to go. If you'd like to use an LLM other than Anthropic's Claude 3.5 Sonnet—like OpenAI's ChatGPT or a local ollama model—to power the pal, see the [Getting started with pal](https://simonpcouch.github.io/pal/articles/pal.html) vignette.
**2)** Then, you need to configure pal with an [ellmer](https://ellmer.tidyverse.org/) model. pal uses ellmer under the hood, so any model that you can chat with through ellmer is also supported by pal. To configure pal with ellmer, set the option `.pal_chat` to any ellmer Chat. For example, to use Claude, you'd write `options(.pal_chat = ellmer::chat_claude()`, possibly in your `.Rprofile` so that pal is ready to go every time you start R. To learn more, see the [Getting started with pal](https://simonpcouch.github.io/pal/articles/pal.html) vignette.

Pals are interfaced with the via the pal addin. For easiest access, we recommend registering the pal addin to a keyboard shortcut.
**3)** Pals are interfaced with the via the pal addin. For easiest access, we recommend registering the pal addin to a keyboard shortcut.

**In RStudio**, navigate to `Tools > Modify Keyboard Shortcuts > Search "Pal"`—we suggest `Ctrl+Alt+P` (or `Ctrl+Cmd+P` on macOS).

Expand Down
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,26 @@ watch your code be rewritten.

## Installation

You can install pal like so:
Getting started with pal takes three steps.

**1)** Install the pal package like so:

``` r
pak::pak("simonpcouch/pal")
```

Then, ensure that you have an
[`ANTHROPIC_API_KEY`](https://console.anthropic.com/) environment
variable set, and you’re ready to go. If you’d like to use an LLM other
than Anthropic’s Claude 3.5 Sonnet—like OpenAI’s ChatGPT or a local
ollama model—to power the pal, see the [Getting started with
**2)** Then, you need to configure pal with an
[ellmer](https://ellmer.tidyverse.org/) model. pal uses ellmer under the
hood, so any model that you can chat with through ellmer is also
supported by pal. To configure pal with ellmer, set the option
`.pal_chat` to any ellmer Chat. For example, to use Claude, you’d write
`options(.pal_chat = ellmer::chat_claude()`, possibly in your
`.Rprofile` so that pal is ready to go every time you start R. To learn
more, see the [Getting started with
pal](https://simonpcouch.github.io/pal/articles/pal.html) vignette.

Pals are interfaced with the via the pal addin. For easiest access, we
recommend registering the pal addin to a keyboard shortcut.
**3)** Pals are interfaced with the via the pal addin. For easiest
access, we recommend registering the pal addin to a keyboard shortcut.

**In RStudio**, navigate to
`Tools > Modify Keyboard Shortcuts > Search "Pal"`—we suggest
Expand Down
38 changes: 9 additions & 29 deletions man/dot-init_pal.Rd

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

4 changes: 3 additions & 1 deletion man/pal_options.Rd

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

25 changes: 25 additions & 0 deletions tests/testthat/_snaps/pal-class.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,28 @@
Error in `user_turn()`:
! Must supply at least one input.

# fetch_pal_chat returns early with old options

Code
.res <- fetch_pal_chat()
Message
pal now uses the option .pal_chat instead of .pal_fn and .pal_args.
i Set `options(.pal_chat = boop(model = "x"))` instead.

# fetch_pal_chat returns early with no option set

Code
.res <- fetch_pal_chat()
Message
! pal requires configuring an ellmer Chat with the .pal_chat option.
i Set e.g. `options(.pal_chat = ellmer::chat_claude()` in your '~/.Rprofile' and restart R.
i See "Choosing a model" in `vignette("pal", package = "pal")` to learn more.

# fetch_pal_chat returns early with bad config

Code
.res <- fetch_pal_chat()
Message
! The option .pal_chat must be an ellmer Chat object, not a string.
i See "Choosing a model" in `vignette("pal", package = "pal")` to learn more.

10 changes: 1 addition & 9 deletions tests/testthat/_snaps/pal-init.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,11 @@
# can use other models

Code
.init_pal("cli", fn = "chat_openai", model = "gpt-4o-mini")
.init_pal("cli", .pal_chat = ellmer::chat_openai(model = "gpt-4o-mini"))
Message

-- A cli pal using gpt-4o-mini.

---

Code
.init_pal("cli")
Message

-- A cli pal using claude-3-5-sonnet-latest.

# errors informatively with bad role

Code
Expand Down
Loading
Loading