R/createSpecifications4R.R

Defines functions createSpecifications4R

Documented in createSpecifications4R

#' Create Specifications for R Function
#'
#' @title Create Specifications for R Function
#' @description This function generates specifications for an R function from your selected text or clipboard.
#'    It takes in a text input, model name, verbosity, and tone speed to generate the specifications.
#' @param Model A character string specifying the GPT model to be used. Default is "gpt-5-nano".
#' @param SelectedCode A logical flag to indicate whether to read from RStudio's selected text. Default is TRUE.
#' @param verbose A logical value indicating whether to print the output. Default is TRUE.
#' @param SlowTone A logical value indicating whether to print the output slowly. Default is FALSE.
#' @importFrom assertthat assert_that is.string noNA
#' @importFrom rstudioapi isAvailable getActiveDocumentContext
#' @importFrom clipr read_clip write_clip
#' @return The function prints the generated specifications to the console.
#' @export createSpecifications4R
#' @author Satoshi Kume
#' @examples
#' \dontrun{
#' # Option 1
#' # Select some text in RStudio and then run the rstudio addins
#' # Option 2
#' # Copy the text into your clipboard then execute
#' clipr::write_clip("A function which can compute the mean of a vector of any size")
#' createSpecifications4R(Model = "gpt-5-nano", SelectedCode = FALSE, verbose = TRUE, SlowTone = FALSE)
#' }
#'
#'

createSpecifications4R <- function(Model = "gpt-5-nano",
                                   SelectedCode = TRUE,
                                   verbose = TRUE,
                                   SlowTone = FALSE) {

  # Get input either from RStudio or clipboard
  if(SelectedCode){
    assertthat::assert_that(rstudioapi::isAvailable())
    input <- rstudioapi::getActiveDocumentContext()$selection[[1]]$text
  } else {
    input <- paste0(clipr::read_clip(), collapse = " \n")
  }

  if(verbose){
    cat("\n", "createSpecifications4R: ", "\n")
    pb <- utils::txtProgressBar(min = 0, max = 3, style = 3)
  }

  # Assertions for input validation
  assertthat::assert_that(
    assertthat::is.string(input),
    assertthat::noNA(input),
    Sys.getenv("OPENAI_API_KEY") != ""
  )

  # Initialize temperature
  temperature = 1
  if(verbose){utils::setTxtProgressBar(pb, 1)}

  # Create template for the prompt
  template = "
  You are an excellent assistant and a highly skilled genius R programmer to build specifications of R function.
  You should always carefully understand the intent of the ideas and concepts you are given,
  and be prepared to be specific in your specifications in an appropriate and comprehensive manner.
  You need to prepare an R function requirements specification for project and technical overview, main functions,
  input parameters, output parameters, use cases or applications, and constraints.
  Finally, you need to make proposals for items missing from the above requirements definition.
  You are sure to output only the deliverables in the requirements definition.
  The language used in the output deliverables must be the same as the language of the following input.
  "

  template1 = "
  Please provide an overview of the requirements definition for an R function based on the following input.:
  "

  # Substitute arguments into the prompt
  template1s <- paste0(template1, paste0(input, collapse = " "), sep=" ")

  # Create prompt history
  history <- list(list('role' = 'system', 'content' = template),
                  list('role' = 'user', 'content' = template1s))

  if(verbose){utils::setTxtProgressBar(pb, 2)}

  # Execute the chat model
  res_df <- chat4R_history(history=history,
                          Model = Model,
                          temperature = temperature)

  # Extract content from data.frame with enhanced validation
  if (is.null(res_df) || !is.data.frame(res_df) || !"content" %in% names(res_df) ||
      is.null(res_df$content) || length(res_df$content) == 0) {
    stop("Invalid or empty response from chat4R_history", call. = FALSE)
  }

  # Convert to character with robust error handling to prevent AI output randomness issues
  res <- tryCatch({
    # Handle list or nested structures from AI response
    if (is.list(res_df$content) && !is.data.frame(res_df$content)) {
      res_df$content <- unlist(res_df$content)
    }

    # Convert to character
    res_char <- as.character(res_df$content)

    # Collapse multiple elements if they exist
    if (length(res_char) > 1) {
      res_char <- paste(res_char, collapse = " ")
    }

    # Validate conversion result: must be single non-NA character
    if (is.na(res_char) || !is.character(res_char) || length(res_char) != 1) {
      stop("Conversion to character failed", call. = FALSE)
    }

    # Trim whitespace and validate non-empty content
    res_char <- trimws(res_char)
    if (nchar(res_char) == 0) {
      stop("Response content is empty after trimming", call. = FALSE)
    }

    res_char
  }, error = function(e) {
    stop(paste("Failed to process AI response:", e$message), call. = FALSE)
  })

  if(verbose){
    utils::setTxtProgressBar(pb, 3)
    cat("\n\n")}

  # Output
  if(SelectedCode){
    rstudioapi::insertText(text = res)
  } else {
  if(verbose) {
    # Attempt slow print with fallback to regular print on error
    tryCatch({
      if(SlowTone) {
        d <- ifelse(20/nchar(res) < 0.3, 20/nchar(res), 0.3)*stats::runif(1, min = 0.95, max = 1.05)
        slow_print_v2(res, delay = d)
      } else {
        d <- ifelse(10/nchar(res) < 0.15, 10/nchar(res), 0.15)*stats::runif(1, min = 0.95, max = 1.05)
        slow_print_v2(res, delay = d)
      }
    }, error = function(e) {
      # Fallback: display result directly if slow_print_v2 encounters an error
      cat("\nWarning: Slow print failed, displaying result directly:\n")
      cat(res, "\n")
      warning(paste("slow_print_v2 error:", e$message), call. = FALSE)
    })
  }

  return(clipr::write_clip(res))

  }
}

Try the chatAI4R package in your browser

Any scripts or data that you put into this service are public.

chatAI4R documentation built on Jan. 10, 2026, 5:07 p.m.