Skip to content

Commit

Permalink
Merge pull request #78 from calderonsamuel/main
Browse files Browse the repository at this point in the history
Rework of the chat app
  • Loading branch information
JamesHWade committed Apr 24, 2023
2 parents 802e5bd + 3eb1ac9 commit 9290e4f
Show file tree
Hide file tree
Showing 121 changed files with 2,659 additions and 434 deletions.
1 change: 1 addition & 0 deletions .Rbuildignore
Expand Up @@ -11,3 +11,4 @@ _\.new\.png$
^pkgdown$
^cran-comments\.md$
^CRAN-SUBMISSION$
diagram.svg
58 changes: 31 additions & 27 deletions DESCRIPTION
@@ -1,51 +1,55 @@
Package: gptstudio
Type: Package
Package: gptstudio
Title: Use Large Language Models Directly in your Development Environment
Version: 0.1.0.9000
Authors@R:
c(person(given = "Michel",
family = "Nivard",
role = c("aut", "cph"),
email = "m.g.nivard@vu.nl"),
person(given = "James",
family = "Wade",
role = c("aut", "cre", "cph"),
email = "github@jameshwade.com",
comment = c(ORCID = "0000-0002-9740-1905")))
Version: 0.2.0
Authors@R: c(
person("Michel", "Nivard", , "m.g.nivard@vu.nl", role = c("aut", "cph")),
person("James", "Wade", , "github@jameshwade.com", role = c("aut", "cre", "cph"),
comment = c(ORCID = "0000-0002-9740-1905")),
person("Samuel", "Calderon", , "samuel.calderon@uarm.pe", role = c("aut"),
comment = c(ORCID = "0000-0001-6847-1210"))
)
Maintainer: James Wade <github@jameshwade.com>
Description: Large language models are readily accessible via API. This package
lowers the barrier to use the API inside of your development environment.
For more on the API, see <https://platform.openai.com/docs/introduction>.
Description: Large language models are readily accessible via API. This
package lowers the barrier to use the API inside of your development
environment. For more on the API, see
<https://platform.openai.com/docs/introduction>.
License: MIT + file LICENSE
Encoding: UTF-8
LazyData: true
URL: https://github.com/MichelNivard/gptstudio,
https://michelnivard.github.io/gptstudio/
BugReports: https://github.com/MichelNivard/gptstudio/issues
Imports:
assertthat,
bslib (>= 0.4.2),
cli,
dplyr,
colorspace,
fontawesome,
glue,
grDevices,
htmltools,
httr,
jsonlite,
magrittr,
purrr,
rclipboard,
rlang,
rstudioapi (>= 0.12),
shiny,
usethis,
RoxygenNote: 7.2.3
utils,
waiter,
xml2
Suggests:
bslib (>= 0.4.2),
covr,
mockr,
shinytest2,
spelling,
testthat (>= 3.0.0),
uuid,
spelling,
shinytest2,
waiter,
withr
Config/testthat/edition: 3
URL: https://github.com/MichelNivard/gptstudio,
https://michelnivard.github.io/gptstudio/
BugReports: https://github.com/MichelNivard/gptstudio/issues
Config/testthat/parallel: true
Encoding: UTF-8
Language: en-US
LazyData: true
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.3
7 changes: 6 additions & 1 deletion NAMESPACE
Expand Up @@ -8,16 +8,21 @@ export(addin_spelling_grammar)
export(check_api)
export(check_api_connection)
export(check_api_key)
export(get_ide_theme_info)
export(gpt_chat)
export(gpt_chat_in_source)
export(gpt_create)
export(gpt_edit)
export(make_chat_history)
export(html_to_r)
export(html_to_taglist)
export(openai_create_chat_completion)
export(openai_create_completion)
export(openai_create_edit)
export(run_chatgpt_app)
import(cli)
import(htmltools)
import(rlang)
import(shiny)
importFrom(assertthat,assert_that)
importFrom(assertthat,is.count)
importFrom(assertthat,is.number)
Expand Down
2 changes: 1 addition & 1 deletion NEWS.md
@@ -1,4 +1,4 @@
# gptstudio (development version)
# gptstudio 0.2.0

# gptstudio

Expand Down
69 changes: 69 additions & 0 deletions R/add_copy_btns_to_pre.R
@@ -0,0 +1,69 @@
add_copy_btns_to_pre <- function(tag_list) {
tq <- htmltools::tagQuery(tag_list)
tq$
siblings("pre")$
each(add_copy_btn_before_tag)$
allTags()
}

add_copy_btn_before_tag <- function(tag, i) {
tq <- tagQuery(tag)

language <- get_code_language(tq)
code_text <- get_pre_text(tag)
copy_btn_div <- create_copy_btn_div(language,
code_text,
copy_btn_id = paste0("copy_", i))

tq$addAttrs(
style = htmltools::css(`border-radius` = "0 0 5px 5px")
)$before(copy_btn_div)

tq$allTags()
}

get_code_language <- function(tq) {
code_element <- tq$children("code")$selectedTags()[[1]]
tagGetAttribute(code_element, "class") %||% "output"
}

create_copy_btn_div <- function(language,
text_to_copy,
copy_btn_id = "codeCopied") {
tags$div(
class = "d-flex justify-content-between bg-dark",
style = htmltools::css(`border-radius` = "5px 5px 0 0"),
tags$p(
class = "px-2 py-1 m-0 text-muted small",
language
),
create_copy_btn(
class = "btn-secondary btn-sm btn-clipboard",
style = htmltools::css(`border-radius` = "0 5px 0 0"),
inputId = copy_btn_id,
label = tags$span(fontawesome::fa("far fa-clipboard"), "Copy"),
text_to_copy = text_to_copy
)
)
}

create_copy_btn <- function(inputId, label, text_to_copy, ...) {
tag <- rclipboard::rclipButton(
inputId = inputId,
label = label,
clipText = text_to_copy,
...
)

tq <- htmltools::tagQuery(tag)

tq$siblings("button")$removeClass("btn-default")$allTags()
}

get_pre_text <- function(pre_tag) {
pre_tag |>
as.character() |>
xml2::read_html() |>
xml2::xml_find_first("./body/*") |>
xml2::xml_text(trim = TRUE)
}
4 changes: 3 additions & 1 deletion R/addin_chatgpt-in-source.R
Expand Up @@ -10,7 +10,9 @@
#' @examples
#' # Select some text in a source file
#' # Then call the function as an RStudio addin
#' \dontrun{addin_chatgpt_in_source()}
#' \dontrun{
#' addin_chatgpt_in_source()
#' }
addin_chatgpt_in_source <- function() {
cli_inform(c("i" = "Sending query to ChatGPT..."))
gpt_chat_in_source(
Expand Down
132 changes: 79 additions & 53 deletions R/app_chat_gpt.R → R/addin_chatgpt.R
Expand Up @@ -8,17 +8,21 @@
#' @inheritParams shiny::runApp
#' @examples
#' # Call the function as an RStudio addin
#' \dontrun{addin_chatgpt()}
#' \dontrun{
#' addin_chatgpt()
#' }
addin_chatgpt <- function(host = getOption("shiny.host", "127.0.0.1")) {
check_api()
rstudioapi::verifyAvailable()
stopifnot(rstudioapi::hasFun("viewer"))

port <- random_port()
app_dir <- system.file("shiny", package = "gptstudio")
app_dir <- create_tmp_app_dir()

run_app_as_bg_job(appDir = app_dir, job_name = "GPT-Studio", host, port)

if (.Platform$OS.type == "unix") Sys.sleep(1.5)

open_bg_shinyapp(host, port)
}

Expand All @@ -38,46 +42,104 @@ random_port <- function() {

#' 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.
#' 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.
#' @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) {
job_script <- create_tmp_job_script(appDir = appDir, port = port, host = host)
job_script <- create_tmp_job_script(appDir = appDir,
port = port,
host = host)
rstudioapi::jobRunScript(job_script, name = job_name)
cli::cli_alert_success(paste0("'", job_name,"'", " initialized as background job in RStudio"))
cli::cli_alert_success(
paste0("'", job_name, "'", " initialized as background job in RStudio")
)
}


#' 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.
#' 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) {
script_file <- tempfile(fileext = ".R")
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)
}

line <- glue::glue("shiny::runApp(appDir = '{appDir}', port = {port}, host = '{host}')")
create_tmp_app_dir <- function() {
dir <- tempdir()

file_con <- file(script_file)
writeLines(line, con = script_file)
close(file_con)
return(script_file)
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)
}


#' 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.
#' 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.
#' @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) {
url <- glue::glue("http://{host}:{port}")
translated_url <- rstudioapi::translateLocalUrl(url)
translated_url <- rstudioapi::translateLocalUrl(url, absolute = TRUE)

if (host %in% c("127.0.0.1")) {
cli::cli_alert_info("Showing app in 'Viewer' pane")
Expand All @@ -87,39 +149,3 @@ open_bg_shinyapp <- function(host, port) {

rstudioapi::viewer(translated_url)
}




#' Make Chat History
#'
#' This function processes the chat history, filters out system messages, and
#' formats the remaining messages with appropriate styling.
#'
#' @param history A list of chat messages with elements containing 'role' and
#' 'content'.
#'
#' @return A list of formatted chat messages with styling applied, excluding
#' system messages.
#' @export
#' @examples
#' chat_history_example <- list(
#' list(role = "user", content = "Hello, World!"),
#' list(role = "system", content = "System message"),
#' list(role = "assistant", content = "Hi, how can I help?")
#' )
#' make_chat_history(chat_history_example)
make_chat_history <- function(history) {
history <-
purrr::map(history, ~ {
if (.x$role == "system") NULL else .x
}) %>%
purrr::compact()

purrr::map(history, ~ {
list(
shiny::strong(toupper(.x$role)),
shiny::markdown(.x$content)
)
})
}
4 changes: 3 additions & 1 deletion R/addin_comment-code.R
Expand Up @@ -8,7 +8,9 @@
#' @examples
#' # Open a R file in Rstudio
#' # Then call the function as an RStudio addin
#' \dontrun{addin_comment_code()}
#' \dontrun{
#' addin_comment_code()
#' }
addin_comment_code <- function() {
gpt_edit(
model = "code-davinci-edit-001",
Expand Down

0 comments on commit 9290e4f

Please sign in to comment.