{ask} anwsers natural language queries trough diverse actions (not just a text answer as is done by simpler interfaces to LLMs) using optional contextual information.
The function you use decides the type of action, it takes the query as its first argument :
ask()
: Text output through a UI in the viewer paneask_in_place()
: Script editsask_clipboard()
: Text output copied to the clipboardask_terminal()
: Copy a command to the terminalask_tibble()
,ask_numeric()
,ask_boolean()
: Actual R objectsask_open()
: Open a scriptask_smart()
: Anything, will run a first API call to guess which function and context you need, and a second to actually answer the query.
The context is provided as a second argument, and we have a collection of function to make this easy.
context_repo()
: Consider your active repocontext_package()
: Consider any packagecontext_script()
: Consider the active script, or any given scriptcontext_clipboard
: Use clipboard content as contextcontext_url
: Use html content of url as context- … (many more)
It is built on top of chatgpt (default) or llama for now.
Install with:
pak::pak("moodymudskipper/ask")
You’ll need either:
- an OpenAI API key linked to a credit card : https://openai.com/index/openai-api/
- An installation of Ollama : https://ollama.com/download
- Download and install the app
- Check what models are available on https://ollama.com/library
- Call for instance
ollama pull llama3.2
orollama pull llama3.2:1b
to pull a specific model
You may provide a model
argument to any ask*()
question. The default
model at the moment is “gpt-4o” but can be changed by setting for stance
options(ask.model="llama3.2")
.
A quick comparison of gpt-4o (OpenAI) and llama3.1 (Ollama) according to just me.
OpenAI | Ollama | |
---|---|---|
Result relevance | ⭐⭐⭐⭐ | ⭐⭐⭐ |
Change code in place | ⭐⭐⭐ | 🌜 |
Speed | ⭐⭐⭐ | ⭐⭐ |
Data protection | ⭐⭐ | ⭐⭐⭐⭐ (local!) |
Price | 💰 | 🆓 |
ask()
a question to start a conversation
library(ask)
ask("where is the Eiffel tower? 1 sentence")
To follow up a query, no need to store the previous output, we keep it
in a global variable (accessible with last_conversation()
) so you
might simply call follow_up()
.
follow_up("is it tall? 1 sentence")
If you’re not happy with an answer you might ask again. This will replace the latest follow up rather than add an extra message as would be the case if we reran the last command.
again()
To access previous conversation you might call conversation_manager()
,
it will open a shiny app in the viewer where you can browse your
history, continue a conversation or pick one to print and continue with
follow_up()
or again()
ask(
"What S3 methods does this register? just a bullet list",
context_script(system.file("NAMESPACE", package = "ask"))
)
# this works because the package is small enough!
ask(
"how to set environment variables locally using this package? 1 sentence",
context_package("withr")
)
Some contexts are too big, this happens often with context_package()
or context_repo()
.
ask(
"what version is installed and what features were added for this version? 1 sentence",
context_package("rlang")
)
#> Error in `response_data()`:
#> ! Request too large for gpt-4o in organization org-Q6n98iXzy6UPHiel732RUDnh on tokens per min (TPM): Limit 30000, Requested 151125. The input or output tokens must be reduced in order to run successfully. Visit https://platform.openai.com/account/rate-limits to learn more.
In these cases we can sometimes manage the context window. In the case
of context_package()
code is not included by default but the help
files are and they sometimes take too many tokens for a LLM query.
ask(
"what version is installed and what features were added for this version? 1 sentence",
context_package("rlang", help_files = FALSE)
)
ask_tibble(
"extract the key dates and events",
context_url("https://en.wikipedia.org/wiki/R_(programming_language)")
)
#> # A tibble: 3 × 2
#> date event
#> <chr> <chr>
#> 1 August 1993 Posted a binary of R on StatLib
#> 2 December 5, 1997 R became a GNU project
#> 3 February 29, 2000 First official 1.0 version released
# simulate clipboard use
clipr::write_clip(
"Grocery list: 2 apples, 1 banana, 7 carrots",
allow_non_interactive = TRUE
)
ask_numeric("How many fruits will I buy ?", context_clipboard())
#> [1] 3
# this was run manually (not reproducible because of context and output)
ask_terminal("what git command can I run to check would did the latest changes to the `ask()` function?", context_repo())
Even more interesting is using those to change your code in place, for
this we use the ask_in_place()
function.
Try things like :
ask_in_place("write roxygen2 documentation for functions in the active script", context_script())
ask_in_place("write tests for my_function()", context_script())
ask_in_place("move my_function() to its own script", context_script())
ask_in_place("write a README for this package", context_repo())
Caching is useful to spare tokens and because LLMs don’t always answer the same thing when asked twice.
There’s a cache
argument to most functions but the recommended use
would be to set options(ask.cache = "my_cache_folder")
. Then ask()
will return the same thing when called with the same prompt and other
parameters, calling again()
on a conversation triggers a new answer
and replaces the cache.
“ram” is a special value to store the cache in memory rather than disk.
If no input is provided we use speech to text, to end either click on “Done” or say “stop listening”. At the moment this only works on macOS and if Chrome is installed.
ask() # ask a question