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