Skip to content

Commit 336d83c

Browse files
authored
Add ask_*() functions to connect to AI Assistant Chats (#41)
* Add ask_*() GenAI URLs * README update with AI
1 parent 3365b98 commit 336d83c

22 files changed

+1602
-18
lines changed

DESCRIPTION

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,19 @@ BugReports: https://github.com/coatless-rpkg/searcher/issues
2323
Depends: R (>= 3.3.0)
2424
License: GPL (>= 2)
2525
Encoding: UTF-8
26-
RoxygenNote: 7.3.1
26+
RoxygenNote: 7.3.2
2727
Roxygen: list(markdown = TRUE)
2828
Suggests:
2929
testthat (>= 2.1.0),
3030
covr,
3131
knitr,
3232
rmarkdown
3333
VignetteBuilder: knitr
34+
Collate:
35+
'ai-prompts.R'
36+
'index-sites.R'
37+
'ai-search-functions.R'
38+
'defunct-functions.R'
39+
'search-functions.R'
40+
'searcher-package.R'
41+
'utilities.R'

NAMESPACE

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# Generated by roxygen2: do not edit by hand
22

3+
export(ai_prompt)
4+
export(ai_prompt_active)
5+
export(ai_prompt_clear)
6+
export(ai_prompt_list)
7+
export(ai_prompt_register)
8+
export(ai_prompt_remove)
9+
export(ask_bing_copilot)
10+
export(ask_chatgpt)
11+
export(ask_claude)
12+
export(ask_copilot)
13+
export(ask_meta_ai)
14+
export(ask_mistral)
15+
export(ask_perplexity)
316
export(search_bb)
417
export(search_bing)
518
export(search_bitbucket)

NEWS.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,23 @@
22

33
## Features
44

5+
- Added GenAI Search Portals:
6+
- `ask_chatgpt()`: Searches with ChatGPT
7+
- `ask_claude()`: Searches with Claude AI
8+
- `ask_perplexity()`: Searches with Perplexity AI
9+
- `ask_mistral()`: Searches with Mistral AI
10+
- `ask_copilot()`: Searches with Microsoft Bing's Copilot
11+
- `ask_meta_ai()`: Searches with Meta AI
12+
- Added an AI Prompt Management System with Persona Prompts
13+
- `ai_prompt()`: Set a prompt for the AI
14+
- `ai_prompt_active()`: View the active prompt
15+
- `ai_prompt_clear()`: Clear the active prompt
16+
- `ai_prompt_list()`: List all prompts
17+
- `ai_prompt_register()`: Add a custom prompt
18+
- `ai_prompt_remove()`: Remove a prompt
19+
- Added new vignettes:
20+
- `search-with-ai-assistants.Rmd`: Overview of the AI Searching Techniques
21+
- `managing-ai-prompts.Rmd`: Overview of `searcher`'s AI Prompt Management System
522
- Added searcher logo ([#40](https://github.com/coatless-rpkg/searcher/pull/40))
623

724

R/ai-prompts.R

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
#' AI Prompt Management System
2+
#'
3+
#' @description
4+
#' A set of functions to manage and apply prompts for AI search services.
5+
#' These functions allow you to create, maintain, and use a library of
6+
#' effective prompts for different AI assistants and scenarios.
7+
#'
8+
#' @details
9+
#' The prompt management system works with multiple levels of prompts:
10+
#'
11+
#' 1. System-level prompt: Set with `ai_prompt()`, applies across all AI services
12+
#' 2. Service-specific prompts: Set with `options()` or in function calls
13+
#' 3. Default prompts: Built-in prompts that ship with the package
14+
#'
15+
#' When a search is performed, prompts are applied in this order, with the
16+
#' query at the end.
17+
#'
18+
#' @name ai_prompt_management
19+
NULL
20+
21+
# Global variable to store the current system prompt
22+
.searcher_system_prompt <- new.env(parent = emptyenv())
23+
.searcher_system_prompt$active <- NULL
24+
.searcher_system_prompt$name <- NULL
25+
26+
# Default prompt library
27+
.searcher_prompt_library <- list(
28+
general = "As an R expert, help with the following question or error:",
29+
debugging = "You are an R debugging expert. First identify what's wrong with this code without fixing it. Then explain why it's wrong and what concept I'm misunderstanding. Finally, provide a working solution with an explanation of why it works.",
30+
learning = "As an R educator teaching a diverse classroom, explain this concept in multiple ways: 1) Start with an intuitive explanation a beginner would understand, 2) Provide a simple working example, 3) Explain how this concept connects to other R concepts, 4) Show a more advanced practical application with commented code.",
31+
package_selection = "As an unbiased R consultant familiar with the entire CRAN ecosystem, compare the top 3-4 R packages for this task. For each package, discuss: 1) Key strengths and limitations, 2) Ease of use and learning curve, 3) Community support and maintenance status, 4) Performance characteristics, 5) Unique features. Conclude with situational recommendations.",
32+
code_review = "As a senior R developer conducting a code review: 1) Note what the code does correctly, 2) Identify potential issues in correctness, performance, readability, and maintainability, 3) Suggest specific improvements with before/after code examples, 4) If relevant, mention R idioms or functions that would simplify the code.",
33+
stats_analysis = "As both a statistician and R programmer, help me with this analysis task. First, explain the appropriate statistical approach and why it's suitable. Then, provide an R implementation with explanations of each step, how to interpret the outputs, and diagnostic checks.",
34+
visualization = "As a data visualization expert who specializes in R: 1) Recommend 2-3 visualization types that would best represent this data and explain why, 2) For the most appropriate visualization, provide ggplot2 code with a clear aesthetic mapping rationale, 3) Suggest specific customizations to improve readability."
35+
)
36+
37+
#' Set or View Active System-level AI Prompt
38+
#'
39+
#' @description
40+
#' Sets a system-level prompt to be used with all AI search functions.
41+
#' When called with no arguments, returns the currently active prompt.
42+
#'
43+
#' @param prompt_name Name of a prompt from the prompt library, or a custom prompt text.
44+
#' Use `ai_prompt_list()` to see available prompt names.
45+
#' If NULL, returns the current active prompt without changing it.
46+
#'
47+
#' @return Invisibly returns the active prompt text. If called without arguments,
48+
#' returns the active prompt visibly.
49+
#'
50+
#' @examples
51+
#' \dontrun{
52+
#' # Set a predefined prompt
53+
#' ai_prompt("debugging")
54+
#'
55+
#' # Set a custom prompt
56+
#' ai_prompt("Explain this R error in simple terms with examples:")
57+
#'
58+
#' # Check current active prompt
59+
#' ai_prompt()
60+
#'
61+
#' # Clear the system prompt
62+
#' ai_prompt(NULL)
63+
#' }
64+
#'
65+
#' @export
66+
ai_prompt <- function(prompt_name = NULL) {
67+
# If no prompt_name is provided, return the current active prompt
68+
if (is.null(prompt_name)) {
69+
return(ai_prompt_active())
70+
}
71+
72+
# If prompt_name is NA, clear the prompt
73+
if (identical(prompt_name, NA)) {
74+
.searcher_system_prompt$active <- NULL
75+
.searcher_system_prompt$name <- NULL
76+
message("System prompt cleared.")
77+
return(invisible(NULL))
78+
}
79+
80+
# Check if prompt_name is in the library
81+
if (prompt_name %in% names(.searcher_prompt_library)) {
82+
prompt_text <- .searcher_prompt_library[[prompt_name]]
83+
prompt_source <- prompt_name
84+
} else {
85+
# Assume it's a custom prompt text
86+
prompt_text <- prompt_name
87+
prompt_source <- "custom"
88+
}
89+
90+
# Set the prompt
91+
.searcher_system_prompt$active <- prompt_text
92+
.searcher_system_prompt$name <- prompt_source
93+
94+
message("Set system prompt to: ", ifelse(prompt_source == "custom",
95+
"custom prompt",
96+
paste0('"', prompt_source, '"')))
97+
invisible(prompt_text)
98+
}
99+
100+
#' Get Currently Active System Prompt
101+
#'
102+
#' @description
103+
#' Returns the currently active system-level prompt, if any.
104+
#'
105+
#' @return The active prompt text, or NULL if no system prompt is set.
106+
#'
107+
#' @examples
108+
#' \dontrun{
109+
#' # Check current active prompt
110+
#' ai_prompt_active()
111+
#' }
112+
#'
113+
#' @export
114+
ai_prompt_active <- function() {
115+
active_prompt <- .searcher_system_prompt$active
116+
if (is.null(active_prompt)) {
117+
message("No system prompt is currently active.")
118+
return(NULL)
119+
} else {
120+
source_info <- .searcher_system_prompt$name
121+
if (source_info == "custom") {
122+
message("Active system prompt (custom):")
123+
} else {
124+
message("Active system prompt (", source_info, "):")
125+
}
126+
return(active_prompt)
127+
}
128+
}
129+
130+
#' List Available AI Prompts
131+
#'
132+
#' @description
133+
#' Lists all available prompts in the prompt library.
134+
#'
135+
#' @return A named list of available prompts.
136+
#'
137+
#' @examples
138+
#' \dontrun{
139+
#' # List all available prompts
140+
#' ai_prompt_list()
141+
#' }
142+
#'
143+
#' @export
144+
ai_prompt_list <- function() {
145+
if (length(.searcher_prompt_library) == 0) {
146+
message("No prompts found in the library.")
147+
return(invisible(NULL))
148+
}
149+
150+
cat("Available AI prompts:\n\n")
151+
for (name in names(.searcher_prompt_library)) {
152+
cat(sprintf("- %s\n", name))
153+
}
154+
155+
invisible(.searcher_prompt_library)
156+
}
157+
158+
#' Register a New AI Prompt
159+
#'
160+
#' @description
161+
#' Adds a new prompt to the prompt library.
162+
#'
163+
#' @param name Name for the new prompt.
164+
#' @param prompt_text The prompt text to register.
165+
#' @param overwrite Whether to overwrite an existing prompt with the same name. Default is FALSE.
166+
#'
167+
#' @return Invisibly returns the updated prompt library.
168+
#'
169+
#' @examples
170+
#' \dontrun{
171+
#' # Register a new prompt
172+
#' ai_prompt_register(
173+
#' "tidyverse",
174+
#' paste("As a tidyverse expert, explain how to solve this problem using",
175+
#' "dplyr, tidyr, and other tidyverse packages:"
176+
#' )
177+
#' )
178+
#' }
179+
#'
180+
#' @export
181+
ai_prompt_register <- function(name, prompt_text, overwrite = FALSE) {
182+
if (!is.character(name) || length(name) != 1) {
183+
stop("Prompt name must be a single character string.")
184+
}
185+
186+
if (!is.character(prompt_text) || length(prompt_text) != 1) {
187+
stop("Prompt text must be a single character string.")
188+
}
189+
190+
if (name %in% names(.searcher_prompt_library) && !overwrite) {
191+
stop("A prompt named '", name, "' already exists. Use overwrite = TRUE to replace it.")
192+
}
193+
194+
.searcher_prompt_library[[name]] <- prompt_text
195+
message("Registered prompt: ", name)
196+
invisible(.searcher_prompt_library)
197+
}
198+
199+
#' Remove an AI Prompt from the Library
200+
#'
201+
#' @description
202+
#' Removes a prompt from the prompt library.
203+
#'
204+
#' @param name Name of the prompt to remove.
205+
#'
206+
#' @return Invisibly returns the updated prompt library.
207+
#'
208+
#' @examples
209+
#' \dontrun{
210+
#' # Remove a prompt
211+
#' ai_prompt_remove("tidyverse")
212+
#' }
213+
#'
214+
#' @export
215+
ai_prompt_remove <- function(name) {
216+
if (!name %in% names(.searcher_prompt_library)) {
217+
stop("No prompt named '", name, "' found in the library.")
218+
}
219+
220+
.searcher_prompt_library[[name]] <- NULL
221+
message("Removed prompt: ", name)
222+
223+
# If the removed prompt was active, clear it
224+
if (!is.null(.searcher_system_prompt$name) && .searcher_system_prompt$name == name) {
225+
.searcher_system_prompt$active <- NULL
226+
.searcher_system_prompt$name <- NULL
227+
message("The removed prompt was active and has been cleared.")
228+
}
229+
230+
invisible(.searcher_prompt_library)
231+
}
232+
233+
#' Clear the Active System Prompt
234+
#'
235+
#' @description
236+
#' Clears the currently active system-level prompt.
237+
#'
238+
#' @return Invisibly returns NULL.
239+
#'
240+
#' @examples
241+
#' \dontrun{
242+
#' # Clear the system prompt
243+
#' ai_prompt_clear()
244+
#' }
245+
#'
246+
#' @export
247+
ai_prompt_clear <- function() {
248+
.searcher_system_prompt$active <- NULL
249+
.searcher_system_prompt$name <- NULL
250+
message("System prompt cleared.")
251+
invisible(NULL)
252+
}
253+
254+
# Helper function to get the active prompts with sources
255+
get_prompt_info <- function(service_name, user_prompt) {
256+
system_prompt <- .searcher_system_prompt$active
257+
system_prompt_name <- .searcher_system_prompt$name
258+
259+
service_option_name <- paste0("searcher.", service_name, "_prompt")
260+
service_default_prompt <- getOption(service_option_name, "")
261+
262+
prompts <- list()
263+
prompt_sources <- character()
264+
265+
# Add system prompt if present
266+
if (!is.null(system_prompt) && nchar(system_prompt) > 0) {
267+
prompts <- c(prompts, system_prompt)
268+
if (system_prompt_name == "custom") {
269+
prompt_sources <- c(prompt_sources, "system (custom)")
270+
} else {
271+
prompt_sources <- c(prompt_sources, paste0("system (", system_prompt_name, ")"))
272+
}
273+
}
274+
275+
# Add user prompt if present, or service default if not
276+
if (!is.null(user_prompt) && nchar(user_prompt) > 0) {
277+
prompts <- c(prompts, user_prompt)
278+
prompt_sources <- c(prompt_sources, "function call")
279+
} else if (nchar(service_default_prompt) > 0) {
280+
prompts <- c(prompts, service_default_prompt)
281+
prompt_sources <- c(prompt_sources, paste0("default (", service_name, ")"))
282+
}
283+
284+
list(
285+
prompts = prompts,
286+
sources = prompt_sources
287+
)
288+
}

0 commit comments

Comments
 (0)