library(shiny)
library(httr)
library(jsonlite)
library(DT)
library(stringr)
library(tidyverse)
library(bslib)
library(janitor)
library(shinyjs)
library(shinycssloaders)
# Internal API key (replace with your actual API key)
<- Sys.getenv("OPENAI_API_KEY")
OPENAI_API_KEY
<- page_fluid(
ui = bs_theme(version = 5),
theme # Add this to use shinyjs
useShinyjs(),
br(),"AI Dataset Generator"),
titlePanel(
sidebarLayout(
sidebarPanel("description", "Describe the dataset you want",
textInput(= "e.g., health data for a family of 4"),
placeholder "generate", "Generate Dataset"),
actionButton("download", "Download CSV")), # Hide download button initially
hidden(downloadButton(
br(), br(),"summary"),
uiOutput(
hr(),"Note: Generated data may not be accurate or suitable for real-world use. The maximum number of records is limited to 25.")
tags$small(
),
mainPanel(
navset_tab("Data Table",
nav_panel(
br(),"dataset")
DTOutput(
)
)
)
)
)
<- function(input, output, session) {
server <- reactiveVal(NULL)
dataset <- reactiveVal("")
summary_text
<- function(csv_string) {
preprocess_csv # Extract only the CSV part
<- "(?s)(.+?\\n(?:[^,\n]+(?:,[^,\n]+)*\n){2,})"
csv_pattern <- str_extract(csv_string, csv_pattern)
csv_match
if (is.na(csv_match)) {
"No valid CSV data found in the response")
stop(
}
<- str_split(csv_match, "\n")[[1]]
lines <- lines[lines != ""] # Remove empty lines
lines
# Get the number of columns from the header
<- str_split(lines[1], ",")[[1]]
header <- length(header)
num_cols
# Ensure all rows have the same number of columns
<- sapply(lines[-1], function(line) { # Skip header
processed_lines <- str_split(line, ",")[[1]]
cols if (length(cols) < num_cols) {
<- c(cols, rep("", num_cols - length(cols)))
cols else if (length(cols) > num_cols) {
} <- cols[1:num_cols]
cols
}
cols
})
# Create a tibble
!!!setNames(as.list(as.data.frame(t(processed_lines))), header))
tibble(
}
<- function(df) {
generate_summary <- paste("Summarize the following dataset:\n\n",
prompt "Dimensions: ", nrow(df), "rows and", ncol(df), "columns\n\n",
"Variables:\n", paste(names(df), collapse=", "), "\n\n",
"Please provide a brief summary of the dataset dimensions and variable definitions. Keep it concise, about 3-4 sentences.")
<- POST(
response = "https://api.openai.com/v1/chat/completions",
url = paste("Bearer", OPENAI_API_KEY)),
add_headers(Authorization
content_type_json(),= toJSON(list(
body = "gpt-3.5-turbo-0125",
model = list(
messages list(role = "system", content = "You are a helpful assistant that summarizes datasets."),
list(role = "user", content = prompt)
)= TRUE),
), auto_unbox = "json"
encode
)
if (status_code(response) == 200) {
<- content(response)
content <- content$choices[[1]]$message$content
summary return(summary)
else {
} return("Error generating summary. Please try again later.")
}
}
observeEvent(input$generate, {
req(input$description)
showPageSpinner()
<- paste("Generate a fake dataset with at least two variables as a CSV string based on this description:",
prompt "Include a header row. Limit to 25 rows of data. Ensure all rows have the same number of columns. Do not include any additional text or explanations.")
input$description,
<- POST(
response = "https://api.openai.com/v1/chat/completions",
url = paste("Bearer", OPENAI_API_KEY)),
add_headers(Authorization
content_type_json(),= toJSON(list(
body = "gpt-3.5-turbo-0125",
model = list(
messages list(role = "system", content = "You are a helpful assistant that generates fake datasets."),
list(role = "user", content = prompt)
)= TRUE),
), auto_unbox = "json"
encode
)
if (status_code(response) == 200) {
<- content(response)
content <- content$choices[[1]]$message$content
csv_string
tryCatch({# Preprocess the CSV string and create a tibble
<- preprocess_csv(csv_string) %>% clean_names() %>%
df ~ ifelse(suppressWarnings(!is.na(as.numeric(.))), as.numeric(.), as.character(.))))
mutate(across(everything(),
dataset(df)"variable", choices = names(df))
updateSelectInput(session,
# Generate and set summary
<- generate_summary(df)
summary
summary_text(summary)
# Show download button
hidePageSpinner()"download")
shinyjs::show(
= function(e) {
}, error "Error parsing CSV:", e$message), type = "error")
showNotification(paste(
})else {
} "Error generating dataset. Please try again later.", type = "error")
showNotification(
}
# Hide loading spinner
"loading-spinner")
shinyjs::hide(
})
<- renderDT({
output$dataset
req(dataset())= FALSE, options = list(pageLength = 10))
datatable(dataset(), rownames
})
<- downloadHandler(
output$download = function() {
filename "generated_dataset.csv"
},= function(file) {
content
req(dataset())file, row.names = FALSE)
write.csv(dataset(),
}
)
<- renderUI({
output$summary
req(summary_text())
div("Dataset Summary"),
h4(
p(summary_text()),= "background-color: #f0f0f0; padding: 10px; border-radius: 5px;"
style
)
})
}
shinyApp(ui, server)
Deploy a LLM-powered Shiny for R App with Secrets
This tutorial builds a Shiny for R application that is powered by an OpenAI API key before deploying it to Posit Connect Cloud. Code is available on GitHub.
1. Create a new GitHub repository
Sign in to GitHub and create a new, public repository.
2. Start a new RStudio project
In RStudio:
- Click New Project from the File menu
- Select Version Control
- Select Git
- Paste the URL to your repository in the Repository URL field
- Enter a Desired Project directory name
- Confirm or change the subdirectory location
Now that your project is synced with your GitHub repository, you are ready to begin coding.
From the New File dropdown or the New File option from the File menu:
- Select R Script
- Save the blank file as
app.R
3. Build the application
Copy and paste the code below into app.R
.
The application relies on an API key from OpenAI, which is called at the top of app.R
with OPENAI_API_KEY <- Sys.getenv("OPENAI_API_KEY")
.
Using a dedicated environment variable file helps keep your secret key out of the application source code. This is important because we will ultimately push it to a public GitHub repository.
Create a file named .Renviron
and add your API key for this project. Learn more about creating the API key here.
='your-api-key-here' OPENAI_API_KEY
If it doesn’t already exist in your project, create a .gitignore
file so that you don’t accidently share the environment variable file publicly. Add the following to it.
.Rproj.user
.Rhistory
.RData
.Ruserdata .Renviron
4. Preview the application
In RStudio when looking at app.R
, click the Run button. This previews your application locally.
Add dependency file
The last thing you need to do is create a dependency file that will provide Connect Cloud with enough information to rebuild your content during deployment. Although there are multiple approaches to achieve this, Connect Cloud exclusively uses a manifest.json file.
To create this file, load the rsconnect
library and run the writeManifest()
function.
rsconnect::writeManifest()
This function creates a .json file named manifest.json
that will tell Connect Cloud (1) what version of R to use and (2) what packages and versions are required.
5. Push to GitHub
Now that everything looks good and we’ve created a file to help reproduce your local environment, it is time to get the code on GitHub.
- Navigate to the Git tab in your RStudio project
- Click Commit
- Select the checkbox to stage each new or revised file
- Enter a commit message
- Click Commit
- Click the Push icon
Your repository now has everything it needs for Connect Cloud.
6. Deploy to Posit Connect Cloud
Follow the steps below to deploy your project to Connect Cloud.
Sign in to Connect Cloud.
Click the Publish icon button on the top of your Portfolio page
Select Shiny
Select the public repository that you created in this tutorial
Confirm the branch
Select app.R as the primary file
Click Advanced settings
Click “Add variable” under “Configure variables”
In the Name field, add
OPENAI_API_KEY
In the Value field, add your secret OpenAI API key
Click Publish
Publishing will display status updates during the deployment process. You will also find build logs streaming on the lower part of the screen.
Congratulations! You successfully deployed to Connect Cloud and are now able to share the link with others.
7. Republish the application
If you update the code to your application or the underlying data source, commit and push the changes to your GitHub repository.
Once the repository has the updated code, you can republish the application on Connect Cloud by going to your Content List and clicking the republish icon.