Skip to content

Commit

Permalink
Merge pull request #224 from MichelNivard/audio-input
Browse files Browse the repository at this point in the history
Audio input, app layout tweaks, better background startup, use ExtendedTask
  • Loading branch information
JamesHWade authored Aug 31, 2024
2 parents a460619 + 44d56ac commit ea51010
Show file tree
Hide file tree
Showing 69 changed files with 2,990 additions and 773 deletions.
20 changes: 10 additions & 10 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Type: Package
Package: gptstudio
Title: Use Large Language Models Directly in your Development Environment
Version: 0.4.0.9003
Version: 0.4.0.9004
Authors@R: c(
person("Michel", "Nivard", , "[email protected]", role = c("aut", "cph")),
person("James", "Wade", , "[email protected]", role = c("aut", "cre", "cph"),
Expand All @@ -21,37 +21,37 @@ BugReports: https://github.com/MichelNivard/gptstudio/issues
Depends:
R (>= 4.0)
Imports:
assertthat,
bslib (>= 0.6.0),
bsicons,
bslib (>= 0.8.0),
cli,
colorspace,
curl,
fontawesome,
glue,
grDevices,
htmltools,
htmlwidgets,
httr2,
ids,
jsonlite,
magrittr,
purrr,
R6,
R6 (>= 2.0),
rlang,
rstudioapi (>= 0.12),
rvest,
shiny,
shiny (>= 1.9.0),
shiny.i18n,
SSEparser,
stringr (>= 1.5.0),
utils,
waiter,
yaml
Suggests:
AzureRMR,
future,
grDevices,
knitr,
miniUI,
mockr,
promises,
rmarkdown,
rvest,
shinytest2,
spelling,
testthat (>= 3.0.0),
Expand Down
13 changes: 8 additions & 5 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,25 @@ 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)
import(htmlwidgets)
import(httr2)
import(rlang)
import(shiny)
importFrom(R6,R6Class)
importFrom(assertthat,assert_that)
importFrom(assertthat,is.count)
importFrom(assertthat,is.number)
importFrom(assertthat,is.string)
importFrom(glue,glue)
importFrom(htmltools,div)
importFrom(htmltools,tag)
importFrom(htmltools,tagList)
importFrom(htmltools,tags)
importFrom(jsonlite,fromJSON)
importFrom(magrittr,"%>%")
importFrom(shiny,icon)
7 changes: 7 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
- Added claude-3.5-sonnet model from Anthropic.
- Set gpt-4o-mini as default model for OpenAI. #219
- Fixed bugs with Azure OpenAI service. #223
- Add audio input option for chat app. #224
- Fix bug with chat app not loading on linux. #224
- Allow chat app to run in Positron (not yet as background job) #224
- API calls now run async with ExtendedTask. #224
- New styling of chat app. #224
- Add code syntax highlighting to chat app. #224
- Replace curl calls with httr2. #224

## gptstudio 0.4.0

Expand Down
191 changes: 70 additions & 121 deletions R/addin_chatgpt.R
Original file line number Diff line number Diff line change
@@ -1,147 +1,96 @@
#' 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")) {
check_installed(c("miniUI", "future"))
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)

if (rstudioapi::versionInfo()$mode == "server") {
Sys.sleep(3)
}

#' 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)
open_app_in_viewer(host, port)
}

# Helper functions

#' 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")
)
find_available_port <- function() {
safe_ports <- setdiff(3000:8000, c(3659, 4045, 5060, 5061, 6000, 6566, 6665:6669, 6697))
sample(safe_ports, 1)
}


#' 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}')"
)

file_con <- file(script_file)
writeLines(line, con = script_file)
close(file_con)
return(script_file)
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_tmp_app_dir <- function() {
dir <- tempdir()

if (.Platform$OS.type == "windows") {
dir <- gsub(pattern = "[\\]", replacement = "/", x = dir)
}
create_temp_app_file <- function() {
temp_file <- tempfile(fileext = ".R")
ide_colors <- dput(get_ide_theme_info())
code_theme_url <- get_highlightjs_theme()

app_file <- create_tmp_app_file()
file.copy(from = app_file, to = file.path(dir, "app.R"), overwrite = TRUE)
return(dir)
writeLines(
# nolint start
glue::glue(
"ide_colors <- {{paste(deparse(ide_colors), collapse = '\n')}}
ui <- gptstudio:::mod_app_ui('app', ide_colors, '{{code_theme_url}}')
server <- function(input, output, session) {
gptstudio:::mod_app_server('app', ide_colors)
}
shiny::shinyApp(ui, server)",
.open = "{{", .close = "}}"
),
temp_file)
# nolint end
temp_file
}

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

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 @@ -150,17 +99,17 @@ open_bg_shinyapp <- function(host, port) {
cli::cli_alert_info("Showing app in browser window")
}

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, max_seconds = 10) {
request(url) %>%
req_retry(max_seconds = 10, backoff = function(n) 0.2) %>%
req_retry(
max_seconds = max_seconds,
is_transient = \(resp) resp_status(resp) >= 300,
backoff = function(n) 0.2
) %>%
req_perform()
}
1 change: 0 additions & 1 deletion R/addin_comment-code.R
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
#' gptstudio_comment_code()
#' }
gptstudio_comment_code <- function() {

file_ext <- get_file_extension()

task <- glue::glue(
Expand Down
Loading

0 comments on commit ea51010

Please sign in to comment.