Skip to content

Commit

Permalink
revamp bg app startup to work on linux Fixes #179
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesHWade committed Aug 24, 2024
1 parent 45dde96 commit fc80c92
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 249 deletions.
2 changes: 1 addition & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ export(gptstudio_comment_code)
export(gptstudio_create_skeleton)
export(gptstudio_request_perform)
export(gptstudio_response_process)
export(gptstudio_run_chat_app)
export(gptstudio_sitrep)
export(gptstudio_skeleton_build)
export(gptstudio_spelling_grammar)
export(input_audio_clip)
export(openai_create_chat_completion)
export(run_chatgpt_app)
export(transcribe_audio)
import(cli)
import(htmltools)
Expand Down
174 changes: 55 additions & 119 deletions R/addin_chatgpt.R
Original file line number Diff line number Diff line change
@@ -1,147 +1,85 @@
#' Run Chat GPT
#' Run the Chat GPT Shiny App as a background job and show it in the viewer pane
#' Run GPTStudio Chat App
#'
#' @export
#' This function initializes and runs the Chat GPT Shiny App as a background job
#' in RStudio and opens it in the viewer pane or browser window.
#'
#' @param host A character string specifying the host on which to run the app.
#' Defaults to the value of `getOption("shiny.host", "127.0.0.1")`.
#'
#' @return This function does not return a value. It runs the Shiny app as a side effect.
#'
#' @details
#' The function performs the following steps:
#' 1. Verifies that RStudio API is available.
#' 2. Finds an available port for the Shiny app.
#' 3. Creates a temporary directory for the app files.
#' 4. Runs the app as a background job in RStudio.
#' 5. Opens the app in the RStudio viewer pane or browser window.
#'
#' @return This function has no return value.
#' @note This function is designed to work within the RStudio IDE and requires
#' the rstudioapi package.
#'
#' @inheritParams shiny::runApp
#' @export
#'
#' @examples
#' # Call the function as an RStudio addin
#' \dontrun{
#' gptstudio_chat()
#' }
gptstudio_chat <- function(host = getOption("shiny.host", "127.0.0.1")) {
rstudioapi::verifyAvailable()
stopifnot(rstudioapi::hasFun("viewer"))

port <- random_port()
app_dir <- create_tmp_app_dir()

run_app_as_bg_job(appDir = app_dir, job_name = "gptstudio", host, port)
port <- find_available_port()
app_dir <- create_temp_app_dir()

open_bg_shinyapp(host, port)
run_app_background(app_dir, "gptstudio", host, port)
open_app_in_viewer(host, port)
}

# Helper functions

#' Generate a random safe port number
#'
#' This function generates a random port allowed by shiny::runApp.
#'
#' @return A single integer representing the randomly selected safe port number.
random_port <- function() {
all_ports <- 3000:8000
unsafe_ports <- c(3659, 4045, 5060, 5061, 6000, 6566, 6665:6669, 6697)
safe_ports <- setdiff(all_ports, unsafe_ports)
sample(safe_ports, size = 1)
find_available_port <- function() {
safe_ports <- setdiff(3000:8000, c(3659, 4045, 5060, 5061, 6000, 6566, 6665:6669, 6697))
sample(safe_ports, 1)
}


#' Run an R Shiny app in the background
#'
#' This function runs an R Shiny app as a background job using the specified
#' directory, name, host, and port.
#'
#' @param job_name The name of the background job to be created
#' @inheritParams shiny::runApp
#' @return This function returns nothing because is meant to run an app as a
#' side effect.
run_app_as_bg_job <- function(appDir = ".", job_name, host, port) { # nolint
job_script <- create_tmp_job_script(
appDir = appDir,
port = port,
host = host
)
rstudioapi::jobRunScript(job_script, name = job_name)
cli_alert_success(
glue("{job_name} initialized as background job in RStudio")
)
create_temp_app_dir <- function() {
dir <- normalizePath(tempdir(), winslash = "/")
app_file <- create_temp_app_file()
file.copy(app_file, file.path(dir, "app.R"), overwrite = TRUE)
dir
}

create_temp_app_file <- function() {
temp_file <- tempfile(fileext = ".R")
ide_colors <- dput(get_ide_theme_info())

#' Create a temporary job script
#'
#' This function creates a temporary R script file that runs the Shiny
#' application from the specified directory with the specified port and host.
#' @inheritParams shiny::runApp
#' @return A string containing the path of a temporary job script
create_tmp_job_script <- function(appDir, port, host) { # nolint
script_file <- tempfile(fileext = ".R")

line <-
glue::glue(
"shiny::runApp(appDir = '{appDir}', port = {port}, host = '{host}')"
)
writeLines(c(
glue::glue("ide_colors <- {paste(deparse(ide_colors), collapse = '\n')}"),
"ui <- gptstudio:::mod_app_ui('app', ide_colors)",
"server <- function(input, output, session) {",
" gptstudio:::mod_app_server('app', ide_colors)",
"}",
"shiny::shinyApp(ui, server)"
), temp_file)

file_con <- file(script_file)
writeLines(line, con = script_file)
close(file_con)
return(script_file)
temp_file
}

create_tmp_app_dir <- function() {
dir <- tempdir()
run_app_background <- function(app_dir, job_name, host, port) {
job_script <- tempfile(fileext = ".R")
writeLines(glue::glue(
"shiny::runApp(appDir = '{app_dir}', port = {port}, host = '{host}')"
), job_script)

if (.Platform$OS.type == "windows") {
dir <- gsub(pattern = "[\\]", replacement = "/", x = dir)
}

app_file <- create_tmp_app_file()
file.copy(from = app_file, to = file.path(dir, "app.R"), overwrite = TRUE)
return(dir)
}

create_tmp_app_file <- function() {
script_file <- tempfile(fileext = ".R")
ide_theme <- get_ide_theme_info() %>%
dput() %>%
utils::capture.output()

line_theme <- glue::glue(
"ide_colors <- {ide_theme}"
)
line_ui <- glue::glue(
"ui <- gptstudio:::mod_app_ui('app', ide_colors)"
)
line_server <- glue::glue(
"server <- function(input, output, session) {
gptstudio:::mod_app_server('app', ide_colors)
}",
.open = "{{",
.close = "}}"
)
line_run_app <- glue::glue("shiny::shinyApp(ui, server)")

file_con <- file(script_file)

writeLines(
text = c(line_theme, line_ui, line_server, line_run_app),
sep = "\n\n",
con = script_file
)

close(file_con)
return(script_file)
rstudioapi::jobRunScript(job_script, name = job_name)
cli::cli_alert_success("{job_name} initialized as background job in RStudio")
}


#' Open browser to local Shiny app
#'
#' This function takes in the host and port of a local Shiny app and opens the
#' app in the default browser.
#'
#' @param host A character string representing the IP address or domain name of
#' the server where the Shiny app is hosted.
#' @param port An integer representing the port number on which the Shiny app is
#' hosted.
#'
#' @return None (opens the Shiny app in the viewer pane or browser window)
open_bg_shinyapp <- function(host, port) {
open_app_in_viewer <- function(host, port) {
url <- glue::glue("http://{host}:{port}")
translated_url <- rstudioapi::translateLocalUrl(url, absolute = TRUE)

if (host %in% c("127.0.0.1")) {
if (host == "127.0.0.1") {
cli::cli_inform(c(
"i" = "Showing app in 'Viewer' pane",
"i" = "Run {.run rstudioapi::viewer(\"{url}\")} to see it"
Expand All @@ -151,15 +89,13 @@ open_bg_shinyapp <- function(host, port) {
}

if (.Platform$OS.type == "unix") {
wait_for_bg_shinyapp(translated_url)
wait_for_bg_app(translated_url)
}

rstudioapi::viewer(translated_url)
}

# This function makes a request for the app's url and fails
# if doesn't find anything after 10 seconds
wait_for_bg_shinyapp <- function(url) {
wait_for_bg_app <- function(url) {
request(url) %>%
req_retry(max_seconds = 10, backoff = function(n) 0.2) %>%
req_perform()
Expand Down
39 changes: 0 additions & 39 deletions man/create_tmp_job_script.Rd

This file was deleted.

28 changes: 19 additions & 9 deletions man/gptstudio_chat.Rd

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

6 changes: 3 additions & 3 deletions man/run_chatgpt_app.Rd → man/gptstudio_run_chat_app.Rd

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

22 changes: 0 additions & 22 deletions man/open_bg_shinyapp.Rd

This file was deleted.

14 changes: 0 additions & 14 deletions man/random_port.Rd

This file was deleted.

Loading

0 comments on commit fc80c92

Please sign in to comment.