-
Notifications
You must be signed in to change notification settings - Fork 3
Azure OpenAI OAuth - R Shiny chat app #202
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
34871de
7983550
d293074
271f481
85bcecd
ed34cc6
85d2e4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| source("renv/activate.R") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| /.posit/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # R Shiny App using an Azure OpenAI OAuth Integration | ||
|
|
||
| An R Shiny app that uses `ellmer` and `shinychat` to create an interactive chat interface that answers questions about the `palmerpenguins` dataset. The app demonstrates the OAuth credential handoff made possible by the Azure OpenAI OAuth integration which gives the shiny app access to Azure OpenAI resources. | ||
|
|
||
| # Setup | ||
|
|
||
| An Azure OpenAI OAuth Integration must be configured by the Posit Connect administrator using application specific fields from the Azure administrator. | ||
|
|
||
| Alternatively, if testing this shiny app locally, the environment variable `AZURE_OPENAI_API_KEY` must be set. | ||
|
|
||
| # Usage | ||
|
|
||
| Deploy the app to Connect. | ||
|
|
||
| **Note**: | ||
|
|
||
| Only members of the "Connect" group have been assigned the proper data action role to use the Azure OpenAI `gpt-4o-mini` deployment that this app is set up to interact with. Because of this, the primary purpose of the app is to serve as a blueprint for what utilizing the Azure OpenAI OAuth integration might look like. | ||
|
|
||
| To adapt this shiny app to a different azure endpoint and model deployment, the `deployment_id`, `api_version`, and `endpoint` arguments passed to `ellmer::chat_azure_openai()`, must be respecified. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| library(shiny) | ||
| library(shinychat) | ||
| library(ellmer) | ||
| library(palmerpenguins) | ||
| library(connectapi) | ||
| library(bslib) | ||
|
|
||
| data(penguins) | ||
|
|
||
| setup_ui <- bslib::page_fluid( | ||
| theme = bslib::bs_theme(bootswatch = "flatly"), | ||
| bslib::layout_columns( | ||
| col_widths = c(2, 8, 2), | ||
| NULL, | ||
| bslib::card( | ||
| bslib::card_header("Azure OpenAI OAuth Setup Instructions"), | ||
| bslib::card_body( | ||
| tags$h4("Configuration Required"), | ||
| tags$p("This application requires an Azure OpenAI OAuth integration to be properly configured."), | ||
| tags$p("For more detailed instructions, please refer to the ", | ||
| tags$a(href="https://docs.posit.co/connect/admin/integrations/oauth-integrations/azure-openai/", | ||
| "Posit Connect Azure OpenAI OAuth integration documentation", | ||
| target="_blank")) | ||
| ), | ||
| width = "100%", | ||
| class = "shadow" | ||
| ), | ||
| NULL | ||
| ) | ||
| ) | ||
|
|
||
| app_ui <- bslib::page_fluid( | ||
| theme = bslib::bs_theme(bootswatch = "flatly"), | ||
| bslib::layout_columns( | ||
| col_widths = c(2, 8, 2), | ||
| NULL, | ||
| bslib::card( | ||
| bslib::card_header("Palmer Penguins Chat Assistant"), | ||
| shinychat::chat_ui("chat", height = "300px"), | ||
| width = "100%", | ||
| class = "shadow" | ||
| ), | ||
| NULL | ||
| ) | ||
| ) | ||
|
|
||
| screen_ui <- shiny::uiOutput("screen") | ||
|
|
||
|
|
||
| server <- function(input, output, session) { | ||
|
|
||
| # Get Connect OAuth credentials using connectapi | ||
| get_oauth_credentials <- function() { | ||
|
|
||
| if (Sys.getenv("POSIT_PRODUCT") == "CONNECT") { | ||
|
|
||
| client <- connectapi::connect() | ||
|
|
||
| user_session_token <- session$request$HTTP_POSIT_CONNECT_USER_SESSION_TOKEN | ||
| oauth_response <- connectapi::get_oauth_credentials(client, user_session_token) | ||
| credentials <- list(Authorization = paste("Bearer", oauth_response$access_token)) | ||
|
|
||
| } else { | ||
|
|
||
| # Local development mode | ||
| api_key <- Sys.getenv("AZURE_OPENAI_API_KEY") | ||
| if (api_key != "") { | ||
| credentials <- list("api-key" = api_key) | ||
| } else { | ||
| credentials <- NULL | ||
| } | ||
|
|
||
| } | ||
|
|
||
| credentials | ||
|
|
||
| } | ||
|
|
||
| OAUTH_INTEGRATION_ENABLED <- TRUE | ||
| user_session_token <- session$request$HTTP_POSIT_CONNECT_USER_SESSION_TOKEN | ||
|
|
||
| if (!is.null(user_session_token)) { | ||
| # Capture any messages that might contain the error code | ||
| msg <- capture.output( | ||
| try(connectapi::connect(token = user_session_token)), | ||
| type = "message" | ||
| ) | ||
|
|
||
| if (any(grepl("212", msg))) { | ||
| OAUTH_INTEGRATION_ENABLED <- FALSE | ||
| } | ||
| } | ||
|
|
||
|
|
||
| output$screen <- shiny::renderUI({ | ||
| if (OAUTH_INTEGRATION_ENABLED) { | ||
| app_ui | ||
| } else { | ||
| setup_ui | ||
| } | ||
| }) | ||
|
|
||
| # Create Azure OpenAI chat function | ||
| penguin_chat <- function() { | ||
|
|
||
| credentials <- get_oauth_credentials() | ||
|
|
||
| # Create a custom chat function for Azure OpenAI | ||
| azure_chat <- ellmer::chat_azure_openai( | ||
| deployment_id = "gpt-4o-mini", | ||
| api_version = "2024-12-01-preview", | ||
| endpoint = "https://8ul4l3wq0g.openai.azure.com", | ||
|
||
| credentials = credentials, | ||
| system_prompt = paste0( | ||
| "You are a data assistant helping with the Palmer Penguins dataset. ", | ||
| "The dataset contains measurements for Adelie, Chinstrap, and Gentoo penguins observed on islands in the Palmer Archipelago. ", | ||
| "Answer questions about the dataset concisely and accurately. ", | ||
| "The dataset structure is: \n", paste(capture.output(str(penguins)), collapse = "\n") | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| shiny::observeEvent(input$chat_user_input, { | ||
|
|
||
| current_chat <- penguin_chat() | ||
|
|
||
| stream <- current_chat$stream_async(input$chat_user_input) | ||
| shinychat::chat_append("chat", stream) | ||
|
|
||
|
|
||
| }) | ||
|
|
||
| } | ||
|
|
||
| shiny::shinyApp(screen_ui, server) | ||
Uh oh!
There was an error while loading. Please reload this page.