diff --git a/.lintr b/.lintr index 63fade9..da7cb04 100644 --- a/.lintr +++ b/.lintr @@ -1,5 +1,5 @@ linters: linters_with_defaults( - line_length_linter = line_length_linter(100), - object_usage_linter = NULL # Does not work with `box::use()`. + defaults = box.linters::rhino_default_linters, + line_length_linter = line_length_linter(100) ) diff --git a/app/logic/empty_state_utils.R b/app/logic/empty_state_utils.R index 4419781..5718197 100644 --- a/app/logic/empty_state_utils.R +++ b/app/logic/empty_state_utils.R @@ -6,8 +6,6 @@ box::use( div, img, p, - renderUI, - tagAppendAttributes ], ) diff --git a/app/logic/general_utils.R b/app/logic/general_utils.R index 350eaa5..1f5fa47 100644 --- a/app/logic/general_utils.R +++ b/app/logic/general_utils.R @@ -1,7 +1,4 @@ box::use( - config[ - get - ], purrr[ map_chr ], diff --git a/app/logic/llm_utils.R b/app/logic/llm_utils.R new file mode 100644 index 0000000..482f1bb --- /dev/null +++ b/app/logic/llm_utils.R @@ -0,0 +1,128 @@ +box::use( + config[ + get + ], + ellmer, #nolint: we do use this package just in a separate notation [[ ]] + glue[ + glue + ], +) + +#' Check if LLM is enabled +#' +#' This function checks the LLM configuration and returns TRUE if enabled, FALSE otherwise. +#' @return Logical indicating if LLM is enabled +#' @export +is_llm_enabled <- function() { + get("llm")$enabled %||% FALSE +} + +#' Get valid LLM providers +#' @return A character vector of valid LLM providers +get_valid_providers <- function( +) { + ellmer_functions <- ls(ellmer)[ + grepl( + pattern = "^chat_", + x = ls(ellmer) + ) + ] + sub( + pattern = "^chat_", + replacement = "", + x = ellmer_functions + ) +} + +#' Check if the provider is valid +#' @param provider The LLM provider to check +#' @param valid_providers A character vector of valid LLM providers +#' @return Logical indicating if the provider is valid +verify_provider <- function( + provider, + valid_providers = get_valid_providers() +) { + if (!provider %in% valid_providers) { + stop( + glue( + "Invalid LLM provider '{provider}'. ", + "Valid providers are: {paste(valid_providers, collapse = ', ')}" + ) + ) + } + TRUE +} + +#' Get LLM configuration +#' +#' Returns the LLM configuration if LLM is enabled. Otherwise, throws an error. +#' @return A list containing the LLM configuration +get_llm_config <- function() { + if (!is_llm_enabled()) { + stop("Oops! LLM is not enabled in config.yml!") + } + verify_provider( + get("llm")$provider, + get_valid_providers() + ) + get("llm") +} + +#' Get the LLM function based on the provider +#' +#' Extracts the chat function dynamically from the `ellmer` module. +#' @param llm_config Optional configuration for the LLM +#' @return A function to create a chat object +get_llm_function <- function( + llm_config = get_llm_config() +) { + ellmer[[glue("chat_{llm_config$provider}")]] +} + +#' Create a chat object +#' +#' Uses the configured LLM provider and model to create a chat object. +#' @param llm_config Optional configuration for the LLM +#' @return A chat object +#' @export +create_chat_object <- function( + llm_config = get_llm_config() +) { + fun <- get_llm_function(llm_config) + fun( + api_key = llm_config$api_key, + model = llm_config$model, + system_prompt = llm_config$system_prompt, + seed = 42, + api_args = list( + temperature = 0 + ) + ) +} + +#' Invoke LLM help with the logs data.frame +#' @param logs_data A data frame containing log data +#' @return A response from the LLM +#' @export +get_llm_help <- function( + logs_data +) { + chat <- create_chat_object() + chat$chat( + concatenate_logs( + logs_data + ) + ) +} + +#' Concatenate logs +#' @param processed_logs A data frame containing log data +#' @return A string with concatenated log entries +concatenate_logs <- function( + processed_logs +) { + paste( + processed_logs$entries.data, + collapse = "\n" + ) +} diff --git a/app/logic/logs_utils.R b/app/logic/logs_utils.R index ebbec6e..c5aae50 100644 --- a/app/logic/logs_utils.R +++ b/app/logic/logs_utils.R @@ -18,19 +18,18 @@ process_log_data <- function( log_data ) { log_info <- strsplit(log_data, "_-_")[[1]] - status <- get_status_info(log_info[1], log_info[3]) div( - class = glue("log-entry {status[1]}-highlight"), + class = glue("log-entry {log_info[4]}-highlight"), icon( - name = status[2], + name = log_info[5], class = glue( - "log-status {status[1]}-text fa-solid" + "log-status {log_info[4]}-text fa-solid" ), ), div( class = "log-info-block", div( - class = glue("log-info {status[1]}-text"), + class = glue("log-info {log_info[4]}-text"), log_info[3] ), div( @@ -41,15 +40,18 @@ process_log_data <- function( ) } +#' @export get_status_info <- function( output_type, log_data ) { if (output_type == "stdout") { - c("green", "circle-info") + status_list <- list("green", "circle-info") } else if (output_type == "stderr" && check_text_error(log_data)) { - c("red", "circle-xmark") + status_list <- list("red", "circle-xmark") } else { - c("yellow", "circle-info") + status_list <- list("yellow", "circle-info") } + names(status_list) <- c("entries.status", "entries.icon") + status_list } diff --git a/app/main.R b/app/main.R index 9c3ea8d..62a0121 100644 --- a/app/main.R +++ b/app/main.R @@ -62,15 +62,25 @@ server <- function(id) { mod_header$server("header") - selected_app_ <- mod_app_table$server("app_table", app_list)$selected_app_ - - selected_job_ <- mod_job_list$server("job_list", selected_app_)$selected_job_ - - mod_logs$server("logs", selected_app_, selected_job_) + selected_app_ <- mod_app_table$server( + "app_table", + app_list + )$selected_app_ + + selected_job_ <- mod_job_list$server( + "job_list", + selected_app_ + )$selected_job_ + + mod_logs$server( + "logs", + selected_app_, + selected_job_ + ) output$job_list_pane <- shiny$renderUI({ if (!shiny$isTruthy(selected_app_()$guid)) { - return(NULL) + NULL } mod_job_list$ui(ns("job_list")) @@ -78,22 +88,18 @@ server <- function(id) { output$logs_pane <- shiny$renderUI({ if (!is.data.frame(app_list) || nrow(app_list) == 0) { - return( - generate_empty_state_ui( - text = "Oops! Can't read apps from Posit Connect.", - image_path = "app/static/illustrations/missing_apps.svg", - color = branding$colors$primary - ) + generate_empty_state_ui( + text = "Oops! Can't read apps from Posit Connect.", + image_path = "app/static/illustrations/missing_apps.svg", + color = branding$colors$primary ) } if (!shiny$isTruthy(selected_job_()$key)) { - return( - generate_empty_state_ui( - text = "Select an application and a job to view logs.", - image_path = "app/static/illustrations/empty_state.svg", - color = branding$colors$primary - ) + generate_empty_state_ui( + text = "Select an application and a job to view logs.", + image_path = "app/static/illustrations/empty_state.svg", + color = branding$colors$primary ) } diff --git a/app/static/css/app.min.css b/app/static/css/app.min.css index 79ee1dc..fca6d13 100644 --- a/app/static/css/app.min.css +++ b/app/static/css/app.min.css @@ -1 +1 @@ -@import"https://fonts.googleapis.com/css2?family=Maven+Pro:wght@400;600&display=swap";.red-text{color:var(--red)}.green-text{color:var(--green)}.yellow-text{color:var(--yellow)}.red-highlight{background-color:var(--red-highlight)}.green-highlight{background-color:var(--green-highlight)}.yellow-highlight{background-color:var(--yellow-highlight)}.red-text{color:var(--red)}.green-text{color:var(--green)}.yellow-text{color:var(--yellow)}.red-highlight{background-color:var(--red-highlight)}.green-highlight{background-color:var(--green-highlight)}.yellow-highlight{background-color:var(--yellow-highlight)}.logs .rt-td-inner{padding:0 !important}.logs>div{text-align:center}.logs>div .empty-state-container{margin-top:150px}.logs>div .empty-state-container .empty-state-image{width:50%}.logs>div .empty-state-container .empty-state-text{color:var(--grey-text);margin-bottom:40px}.logs-container{position:relative}.logs-container .log-entry{display:flex;align-items:center;gap:20px;padding:10px;margin:5px 10px}.logs-container .log-entry i{font-size:1.5em}.logs-container .log-entry .log-info-block{display:flex;flex-direction:column;gap:10px}.logs-container .log-entry .log-info-block .log-info{font-weight:600}.logs-container .log-entry .log-info-block .log-time{font-size:.75em}.logs-container .logs-download{position:absolute;z-index:2;right:0;margin:10px;background:0;border-radius:0;padding:5px 10px}.wrapper{background:none !important}.content-wrapper{background:var(--white);color:var(--black-text);height:90vh}.dashboard-body{display:flex;flex-direction:column;padding:0}.dashboard-body .dashboard-container{display:flex;flex-direction:row;height:100vh}.dashboard-body .reactable{background:rgba(0,0,0,0)}.dashboard-body .rt-search{width:80%;margin:10px 10px 20px;align-self:center;text-align:center;border-radius:0}.dashboard-body .rt-tr-header{display:none !important}.dashboard-body .rt-tr{align-items:center}.dashboard-body .rt-tr-selected{background:var(--selected-row)}.dashboard-body .app-table{width:30%;height:100%;overflow-y:auto}.dashboard-body .job-list{width:15%;height:100%;overflow-y:auto}.dashboard-body .logs{background:var(--white);width:55%;height:100%;overflow-y:auto}.app-entry{display:flex;flex-direction:column;width:100%}.app-entry .app-title{font-size:1.1em}.app-entry .app-link-icon{font-size:.5em;margin-left:10px;margin-bottom:10px}.app-entry .app-metadata{display:flex;flex-direction:column;gap:5px;color:var(--grey-text);font-size:.75em}.red-text{color:var(--red)}.green-text{color:var(--green)}.yellow-text{color:var(--yellow)}.red-highlight{background-color:var(--red-highlight)}.green-highlight{background-color:var(--green-highlight)}.yellow-highlight{background-color:var(--yellow-highlight)}.job-entry .job-key,.job-entry .job-start-time,.job-entry .job-end-time{font-size:.75em;color:var(--grey-text)}.header{display:flex;width:100%;align-items:center;justify-content:space-between;gap:10px;margin-bottom:20px}.header .header-section{display:flex;align-items:center;gap:10px}.header .left img{width:200px}.header .left h2{margin:0;margin-bottom:5px;margin-left:20px}.header .left .vertical-line{height:50px}.header .right .cta-button{background:var(--primary);color:#fff;padding:10px;border-radius:10px;margin:0 10px}*{font-family:"Maven Pro",sans-serif}body{overflow:hidden}.vertical-line{border-left:1px var(--grey2-border) solid;height:80%;align-self:center} +@import "https://fonts.googleapis.com/css2?family=Maven+Pro:wght@400;600&display=swap";.red-text{color:var(--red)}.green-text{color:var(--green)}.yellow-text{color:var(--yellow)}.red-highlight{background-color:var(--red-highlight)}.green-highlight{background-color:var(--green-highlight)}.yellow-highlight{background-color:var(--yellow-highlight)}.red-text{color:var(--red)}.green-text{color:var(--green)}.yellow-text{color:var(--yellow)}.red-highlight{background-color:var(--red-highlight)}.green-highlight{background-color:var(--green-highlight)}.yellow-highlight{background-color:var(--yellow-highlight)}.logs .rt-td-inner{padding:0 !important}.logs .rt-search{width:85% !important;align-self:flex-start !important}.logs>div{text-align:center}.logs>div .empty-state-container{margin-top:150px}.logs>div .empty-state-container .empty-state-image{width:50%}.logs>div .empty-state-container .empty-state-text{color:var(--grey-text);margin-bottom:40px}.logs-llm-placeholder img{width:50%}.logs-llm-modal .modal-dialog{text-align:justify}.logs-llm-modal .modal-dialog h2,.logs-llm-modal .modal-dialog h3{color:var(--primary)}.logs-llm-modal .modal-dialog .btn{background:var(--red-highlight);border-radius:0;border:1px solid var(--red)}.logs-llm-modal .modal-dialog .modal-content{border-radius:0}.logs-container{position:relative}.logs-container .log-entry{display:flex;align-items:center;gap:20px;padding:10px;margin:5px 10px}.logs-container .log-entry i{font-size:1.5em}.logs-container .log-entry .log-info-block{display:flex;flex-direction:column;gap:10px}.logs-container .log-entry .log-info-block .log-info{font-weight:600}.logs-container .log-entry .log-info-block .log-time{font-size:0.75em}.logs-container .logs-options{position:absolute;z-index:2;right:0;margin:10px}.logs-container .logs-options .logs-options-button{background:0;border-radius:0;padding:5px 10px}.wrapper{background:none !important}.content-wrapper{background:var(--white);color:var(--black-text);height:90vh}.dashboard-body{display:flex;flex-direction:column;padding:0}.dashboard-body .dashboard-container{display:flex;flex-direction:row;height:100vh}.dashboard-body .reactable{background:transparent}.dashboard-body .rt-search{width:80%;margin:10px 10px 20px;align-self:center;text-align:center;border-radius:0}.dashboard-body .rt-tr-header{display:none !important}.dashboard-body .rt-tr{align-items:center}.dashboard-body .rt-tr-selected{background:var(--selected-row)}.dashboard-body .app-table{width:30%;height:100%;overflow-y:auto}.dashboard-body .job-list{width:15%;height:100%;overflow-y:auto}.dashboard-body .logs{background:var(--white);width:55%;height:100%;overflow-y:auto}.app-entry{display:flex;flex-direction:column;width:100%}.app-entry .app-title{font-size:1.1em}.app-entry .app-link-icon{font-size:0.5em;margin-left:10px;margin-bottom:10px}.app-entry .app-metadata{display:flex;flex-direction:column;gap:5px;color:var(--grey-text);font-size:0.75em}.red-text{color:var(--red)}.green-text{color:var(--green)}.yellow-text{color:var(--yellow)}.red-highlight{background-color:var(--red-highlight)}.green-highlight{background-color:var(--green-highlight)}.yellow-highlight{background-color:var(--yellow-highlight)}.job-entry .job-key,.job-entry .job-start-time,.job-entry .job-end-time{font-size:0.75em;color:var(--grey-text)}.header{display:flex;width:100%;align-items:center;justify-content:space-between;gap:10px;margin-bottom:20px}.header .header-section{display:flex;align-items:center;gap:10px}.header .left img{width:200px}.header .left h2{margin:0;margin-bottom:5px;margin-left:20px}.header .left .vertical-line{height:50px}.header .right .cta-button{background:var(--primary);color:white;padding:10px;border-radius:10px;margin:0 10px}*{font-family:"Maven Pro", sans-serif}body{overflow:hidden}.vertical-line{border-left:1px var(--grey2-border) solid;height:80%;align-self:center} diff --git a/app/static/llm_system_prompt.txt b/app/static/llm_system_prompt.txt new file mode 100644 index 0000000..f191670 --- /dev/null +++ b/app/static/llm_system_prompt.txt @@ -0,0 +1,17 @@ +Context: You are an extremely sophisticated, R/Shiny log analyzer and debugger. +Task: You will be given only the problematic logs. Your job is to analyse them. +Result: Generate a div with formatting as given below. +Structure: +-

LogAnalyzer AI Help

+-
+-

Caution: This help is AI generated. Verify the information before taking any action.

+-

Problem Explanation

+- [Add a one- or two-liner explanation here] +-

Suggestions (bullet list of 3 suggestions, be detailed, but no subpoints) +Formatting: +This will be embedded in a modalDialog in a Shiny app. Following this to the last letter is important. Breaking these will get you fired. +- Ensure function, library and related names use tags. +- All code blocks and one-liners should be formatted properly using tags. +- All emphasis should happen with and not _ or *. +- All bold should happen with and not ** or __. +- Return raw HTML. No \```html wrapper. diff --git a/app/styles/_logs.scss b/app/styles/_logs.scss index e0d9d50..1532853 100644 --- a/app/styles/_logs.scss +++ b/app/styles/_logs.scss @@ -5,6 +5,11 @@ padding: 0 !important; } + .rt-search { + width: 85% !important; + align-self: flex-start !important; + } + > div { text-align: center; @@ -23,6 +28,27 @@ } } +.logs-llm-modal { + .modal-dialog { + text-align: justify; + + h2, + h3 { + color: var(--primary); + } + + .btn { + background: var(--red-highlight); + border-radius: 0; + border: 1px solid var(--red); + } + + .modal-content { + border-radius: 0; + } + } +} + .logs-container { position: relative; @@ -52,13 +78,16 @@ } } - .logs-download { + .logs-options { position: absolute; z-index: 2; right: 0; margin: 10px; - background: 0; - border-radius: 0; - padding: 5px 10px; + + .logs-options-button { + background: 0; + border-radius: 0; + padding: 5px 10px; + } } } diff --git a/app/view/mod_header.R b/app/view/mod_header.R index e6a860c..ae5891e 100644 --- a/app/view/mod_header.R +++ b/app/view/mod_header.R @@ -39,7 +39,7 @@ ui <- function(id) { div( class = "right header-section", actionLink( - "lets-talk", + ns("lets-talk"), label = "Let's Talk", class = "cta-button", onclick = "window.open('https://appsilon.com/#contact', '_blank');" diff --git a/app/view/mod_job_list.R b/app/view/mod_job_list.R index 469156c..c1cc2e4 100644 --- a/app/view/mod_job_list.R +++ b/app/view/mod_job_list.R @@ -1,5 +1,4 @@ box::use( - magrittr[`%>%`], reactable[ colDef, getReactableState, diff --git a/app/view/mod_logs.R b/app/view/mod_logs.R index 52b1a15..4644574 100644 --- a/app/view/mod_logs.R +++ b/app/view/mod_logs.R @@ -1,40 +1,47 @@ box::use( - dplyr[mutate], + dplyr[ + as_tibble, + bind_cols, + filter, + mutate, + tibble + ], glue[glue], magrittr[`%>%`], + purrr[ + pmap_dfr + ], reactable[ colDef, reactable, reactableOutput, renderReactable ], + shiny, shinycssloaders[withSpinner], - shiny[ - div, - downloadButton, - downloadHandler, - icon, - moduleServer, - NS, - observeEvent, - reactive, - renderUI, - req, - uiOutput - ], ) box::use( - app/logic/api_utils[download_job_logs, get_job_logs], - app/logic/logs_utils[process_log_data], + app/logic/api_utils[ + download_job_logs, + get_job_logs + ], + app/logic/llm_utils[ + get_llm_help, + is_llm_enabled + ], + app/logic/logs_utils[ + get_status_info, + process_log_data + ], ) #' @export ui <- function(id) { - ns <- NS(id) - div( + ns <- shiny$NS(id) + shiny$div( class = "logs-container", - uiOutput( + shiny$uiOutput( ns("download_logs") ), withSpinner( @@ -48,12 +55,16 @@ ui <- function(id) { } #' @export -server <- function(id, selected_app_, selected_job_) { - moduleServer(id, function(input, output, session) { +server <- function( + id, + selected_app_, + selected_job_ +) { + shiny$moduleServer(id, function(input, output, session) { ns <- session$ns - output$download <- downloadHandler( + output$download <- shiny$downloadHandler( filename = function() { glue( "{selected_app_()$name}_{selected_job_()$id}.txt" @@ -68,41 +79,68 @@ server <- function(id, selected_app_, selected_job_) { } ) - output$download_logs <- renderUI({ + output$download_logs <- shiny$renderUI({ if (is.null(selected_job_()$key)) { - return(NULL) + NULL } - downloadButton( - outputId = ns("download"), - label = NULL, - icon = icon("download"), - class = "logs-download" + shiny$div( + class = "logs-options", + if (is_llm_enabled()) { + shiny$actionButton( + inputId = ns("llm"), + label = NULL, + icon = shiny$icon("robot"), + class = "llm logs-options-button" + ) + }, + shiny$downloadButton( + outputId = ns("download"), + label = NULL, + icon = shiny$icon("download"), + class = "download logs-options-button" + ) ) }) - logs_data <- reactive({ - req(selected_job_()$key) + logs_data <- shiny$reactive({ + shiny$req(selected_job_()$key) get_job_logs( selected_app_()$guid, selected_job_()$key ) }) - output$logs_table <- renderReactable({ - - processed_logs <- logs_data() %>% + processed_logs <- shiny$reactive({ + logs_data() %>% + pmap_dfr( + ~ { + get_status_info(..1, ..3) |> + as_tibble() |> + bind_cols( + tibble( + entries.source = ..1, + entries.timestamp = ..2, + entries.data = ..3 + ) + ) + } + ) %>% mutate( log_line = paste( entries.source, entries.timestamp, entries.data, + entries.status, + entries.icon, sep = "_-_" ) ) + }) + output$logs_table <- renderReactable({ reactable( - data = processed_logs, + data = processed_logs(), searchable = TRUE, borderless = TRUE, pagination = FALSE, @@ -118,6 +156,12 @@ server <- function(id, selected_app_, selected_job_) { entries.data = colDef( show = FALSE ), + entries.status = colDef( + show = FALSE + ), + entries.icon = colDef( + show = FALSE + ), log_line = colDef( name = "Logs", cell = function(log_data) { @@ -128,5 +172,34 @@ server <- function(id, selected_app_, selected_job_) { ) }) + if (is_llm_enabled()) { + llm_result <- shiny$eventReactive(input$llm, { + shiny$req(processed_logs()) + get_llm_help( + processed_logs() %>% + filter( + entries.status %in% c("red", "yellow") + ) + ) + }) + + shiny$observeEvent(llm_result(), { + shiny$removeModal() + shiny$showModal( + shiny$modalDialog( + easyClose = TRUE, + size = "m", + footer = shiny$modalButton( + shiny$icon( + "xmark", + class = "red-text" + ) + ), + shiny$HTML(llm_result()) + ) %>% + shiny$tagAppendAttributes(class = "logs-llm-modal") + ) + }) + } }) } diff --git a/config.yml b/config.yml index 9407a26..462f155 100644 --- a/config.yml +++ b/config.yml @@ -1,6 +1,12 @@ default: rhino_log_level: !expr Sys.getenv("RHINO_LOG_LEVEL", "INFO") rhino_log_file: !expr Sys.getenv("RHINO_LOG_FILE", NA) + llm: + enabled: FALSE + provider: "openai" + model: "gpt-4o" + api_key: !expr Sys.getenv("LLM_API_KEY", NA) + system_prompt: !expr readLines("app/static/llm_system_prompt.txt") |> paste(collapse = " ") app_role: "owner" branding: logo: diff --git a/dependencies.R b/dependencies.R index 03ed7ac..a2db989 100644 --- a/dependencies.R +++ b/dependencies.R @@ -1,5 +1,6 @@ # This file allows packrat (used by rsconnect during deployment) to pick up dependencies. library(dplyr) +library(ellmer) library(httr2) library(magrittr) library(reactable) diff --git a/renv.lock b/renv.lock index a7b9d2a..ec8eafa 100644 --- a/renv.lock +++ b/renv.lock @@ -1,6 +1,6 @@ { "R": { - "Version": "4.4.1", + "Version": "4.5.0", "Repositories": [ { "Name": "CRAN", @@ -61,7 +61,7 @@ }, "R.utils": { "Package": "R.utils", - "Version": "2.12.3", + "Version": "2.13.0", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -72,28 +72,39 @@ "tools", "utils" ], - "Hash": "3dc2829b790254bfba21e60965787651" + "Hash": "d32373d88da809f8974d5307481862b0" }, "R6": { "Package": "R6", - "Version": "2.5.1", + "Version": "2.6.1", "Source": "Repository", - "Repository": "CRAN", + "Repository": "RSPM", "Requirements": [ "R" ], - "Hash": "470851b6d5d0ac559e9d01bb352b4021" + "Hash": "d4335fe7207f1c01ab8c41762f5840d4" }, "Rcpp": { "Package": "Rcpp", - "Version": "1.0.13-1", + "Version": "1.0.14", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "methods", "utils" ], - "Hash": "6b868847b365672d6c1677b1608da9ed" + "Hash": "e7bdd9ee90e96921ca8a0f1972d66682" + }, + "S7": { + "Package": "S7", + "Version": "0.2.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "utils" + ], + "Hash": "5deb66b3ae702137e1f4162c11861e76" }, "askpass": { "Package": "askpass", @@ -182,9 +193,9 @@ }, "bslib": { "Package": "bslib", - "Version": "0.8.0", + "Version": "0.9.0", "Source": "Repository", - "Repository": "CRAN", + "Repository": "RSPM", "Requirements": [ "R", "base64enc", @@ -200,7 +211,7 @@ "rlang", "sass" ], - "Hash": "b299c6741ca9746fb227debcb0f9fb6c" + "Hash": "70a6489cc254171fb9b4a7f130f44dca" }, "cachem": { "Package": "cachem", @@ -228,14 +239,14 @@ }, "cli": { "Package": "cli", - "Version": "3.6.3", + "Version": "3.6.4", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "utils" ], - "Hash": "b21916dd77a27642b447374a5d30ecf3" + "Hash": "491c34d3d9dd0d2fe13d9f278bb90795" }, "codetools": { "Package": "codetools", @@ -249,10 +260,10 @@ }, "commonmark": { "Package": "commonmark", - "Version": "1.9.2", + "Version": "1.9.5", "Source": "Repository", "Repository": "RSPM", - "Hash": "14eb0596f987c71535d07c3aff814742" + "Hash": "4ac08754c8ed35996b7c343fbb22885a" }, "config": { "Package": "config", @@ -264,6 +275,17 @@ ], "Hash": "8b7222e9d9eb5178eea545c0c4d33fc2" }, + "coro": { + "Package": "coro", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "rlang" + ], + "Hash": "4998c8836ff95c90993a4eb8d853df71" + }, "crayon": { "Package": "crayon", "Version": "1.5.3", @@ -278,27 +300,13 @@ }, "curl": { "Package": "curl", - "Version": "6.0.1", + "Version": "6.2.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R" ], - "Hash": "e8ba62486230951fcd2b881c5be23f96" - }, - "cyclocomp": { - "Package": "cyclocomp", - "Version": "1.1.1", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "callr", - "crayon", - "desc", - "remotes", - "withr" - ], - "Hash": "cdc4a473222b0112d4df0bcfbed12d44" + "Hash": "e4f9e10b18f453a1b7eaf38247dad4fe" }, "desc": { "Package": "desc", @@ -315,9 +323,9 @@ }, "diffobj": { "Package": "diffobj", - "Version": "0.3.5", + "Version": "0.3.6", "Source": "Repository", - "Repository": "CRAN", + "Repository": "RSPM", "Requirements": [ "R", "crayon", @@ -326,7 +334,7 @@ "tools", "utils" ], - "Hash": "bcaa8b95f8d7d01a5dedfd959ce88ab8" + "Hash": "e036ce354ab60e705ac5f40bac87e8cb" }, "digest": { "Package": "digest", @@ -362,15 +370,35 @@ ], "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" }, + "ellmer": { + "Package": "ellmer", + "Version": "0.1.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R6", + "S7", + "cli", + "coro", + "glue", + "httr2", + "jsonlite", + "later", + "lifecycle", + "promises", + "rlang" + ], + "Hash": "754d3a8cbb25b05be2058892f579422f" + }, "evaluate": { "Package": "evaluate", - "Version": "1.0.1", + "Version": "1.0.3", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R" ], - "Hash": "3fd29944b231036ad67c3edb32e02201" + "Hash": "e9651417729bbe7472e32b5027370e79" }, "fansi": { "Package": "fansi", @@ -405,14 +433,14 @@ }, "fs": { "Package": "fs", - "Version": "1.6.5", + "Version": "1.6.6", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "methods" ], - "Hash": "7f48af39fa27711ea5fbd183b399920d" + "Hash": "7eb1e342eee7e0a7449c49cdaa526d39" }, "generics": { "Package": "generics", @@ -480,7 +508,7 @@ }, "httpuv": { "Package": "httpuv", - "Version": "1.6.15", + "Version": "1.6.16", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -491,11 +519,11 @@ "promises", "utils" ], - "Hash": "d55aa087c47a63ead0f6fc10f8fa1ee0" + "Hash": "6c3c8728e40326de6529a5c46e377e5c" }, "httr2": { "Package": "httr2", - "Version": "1.0.6", + "Version": "1.1.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -512,7 +540,7 @@ "vctrs", "withr" ], - "Hash": "3ef5d07ec78803475a94367d71b40c41" + "Hash": "ade531519694081d91036b509eb30594" }, "jquerylib": { "Package": "jquerylib", @@ -526,17 +554,17 @@ }, "jsonlite": { "Package": "jsonlite", - "Version": "1.8.9", + "Version": "2.0.0", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "methods" ], - "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" + "Hash": "b0776f526d36d8bd4a3344a88fe165c4" }, "knitr": { "Package": "knitr", - "Version": "1.49", + "Version": "1.50", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -548,18 +576,18 @@ "xfun", "yaml" ], - "Hash": "9fcb189926d93c636dea94fbe4f44480" + "Hash": "5a07d8ec459d7b80bd4acca5f4a6e062" }, "later": { "Package": "later", - "Version": "1.3.2", + "Version": "1.4.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "Rcpp", "rlang" ], - "Hash": "a3e051d405326b8b0012377434c62b37" + "Hash": "9591aabef9cf988f05bde9324fd3ad99" }, "lazyeval": { "Package": "lazyeval", @@ -586,14 +614,14 @@ }, "lintr": { "Package": "lintr", - "Version": "3.1.2", + "Version": "3.2.0", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "backports", + "cli", "codetools", - "cyclocomp", "digest", "glue", "knitr", @@ -603,7 +631,7 @@ "xml2", "xmlparsedata" ], - "Hash": "08cff46381a242d44c0d8dd0aabd9f71" + "Hash": "45b44c7f1040cce8f34ec0a9c92d47f3" }, "logger": { "Package": "logger", @@ -639,23 +667,23 @@ }, "mime": { "Package": "mime", - "Version": "0.12", + "Version": "0.13", "Source": "Repository", - "Repository": "CRAN", + "Repository": "RSPM", "Requirements": [ "tools" ], - "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + "Hash": "0ec19f34c72fab674d8f2b4b1c6410e1" }, "openssl": { "Package": "openssl", - "Version": "2.2.2", + "Version": "2.3.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "askpass" ], - "Hash": "d413e0fef796c9401a4419485f709ca1" + "Hash": "bc54d87ebf858b28de18df4bca6528d3" }, "packrat": { "Package": "packrat", @@ -671,12 +699,11 @@ }, "pillar": { "Package": "pillar", - "Version": "1.9.0", + "Version": "1.10.2", "Source": "Repository", - "Repository": "CRAN", + "Repository": "RSPM", "Requirements": [ "cli", - "fansi", "glue", "lifecycle", "rlang", @@ -684,11 +711,11 @@ "utils", "vctrs" ], - "Hash": "15da5a8412f317beeee6175fbc76f4bb" + "Hash": "1098920a19b5cd5a15bacdc74a89979d" }, "pkgbuild": { "Package": "pkgbuild", - "Version": "1.4.5", + "Version": "1.4.7", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -699,7 +726,7 @@ "desc", "processx" ], - "Hash": "30eaaab94db72652e72e3475c1b55278" + "Hash": "ae47817501cafc99f57586b6e5241134" }, "pkgconfig": { "Package": "pkgconfig", @@ -742,7 +769,7 @@ }, "processx": { "Package": "processx", - "Version": "3.8.4", + "Version": "3.8.6", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -751,11 +778,11 @@ "ps", "utils" ], - "Hash": "0c90a7d71988856bad2a2a45dd871bb9" + "Hash": "720161b280b0a35f4d1490ead2fe81d0" }, "promises": { "Package": "promises", - "Version": "1.3.0", + "Version": "1.3.2", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -767,24 +794,24 @@ "rlang", "stats" ], - "Hash": "434cd5388a3979e74be5c219bcd6e77d" + "Hash": "c84fd4f75ea1f5434735e08b7f50fbca" }, "ps": { "Package": "ps", - "Version": "1.8.1", + "Version": "1.9.1", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "utils" ], - "Hash": "b4404b1de13758dea1c0484ad0d48563" + "Hash": "093688087b0bacce6ba2f661f36328e2" }, "purrr": { "Package": "purrr", - "Version": "1.0.2", + "Version": "1.0.4", "Source": "Repository", - "Repository": "CRAN", + "Repository": "RSPM", "Requirements": [ "R", "cli", @@ -793,7 +820,7 @@ "rlang", "vctrs" ], - "Hash": "1cba04a4e9414bdefc9dcaa99649a8dc" + "Hash": "cc8b5d43f90551fa6df0a6be5d640a4f" }, "rappdirs": { "Package": "rappdirs", @@ -830,20 +857,6 @@ ], "Hash": "6069eb2a6597963eae0605c1875ff14c" }, - "remotes": { - "Package": "remotes", - "Version": "2.5.0", - "Source": "Repository", - "Repository": "RSPM", - "Requirements": [ - "R", - "methods", - "stats", - "tools", - "utils" - ], - "Hash": "3ee025083e66f18db6cf27b56e23e141" - }, "renv": { "Package": "renv", "Version": "1.0.3", @@ -866,7 +879,7 @@ }, "rhino": { "Package": "rhino", - "Version": "1.10.1", + "Version": "1.11.0", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -874,6 +887,7 @@ "box", "box.linters", "box.lsp", + "callr", "cli", "config", "fs", @@ -891,18 +905,18 @@ "withr", "yaml" ], - "Hash": "cd58cf8f362b8cc8c82aea1de9468218" + "Hash": "65daa659ff338dcf4f1ff79ddecadb00" }, "rlang": { "Package": "rlang", - "Version": "1.1.4", + "Version": "1.1.6", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "R", "utils" ], - "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" + "Hash": "892124978869b74935dc3934c42bfe5a" }, "rmarkdown": { "Package": "rmarkdown", @@ -939,7 +953,7 @@ }, "rsconnect": { "Package": "rsconnect", - "Version": "1.3.3", + "Version": "1.3.4", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -958,7 +972,7 @@ "tools", "yaml" ], - "Hash": "d466c98fdce812325feb4ad406c6ca4b" + "Hash": "315454d2f50d117ef58691d0bf50fe63" }, "rstudioapi": { "Package": "rstudioapi", @@ -969,9 +983,9 @@ }, "sass": { "Package": "sass", - "Version": "0.4.9", + "Version": "0.4.10", "Source": "Repository", - "Repository": "CRAN", + "Repository": "RSPM", "Requirements": [ "R6", "fs", @@ -979,13 +993,13 @@ "rappdirs", "rlang" ], - "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + "Hash": "3fb78d066fb92299b1d13f6a7c9a90a8" }, "shiny": { "Package": "shiny", - "Version": "1.9.1", + "Version": "1.10.0", "Source": "Repository", - "Repository": "CRAN", + "Repository": "RSPM", "Requirements": [ "R", "R6", @@ -1012,7 +1026,7 @@ "withr", "xtable" ], - "Hash": "6a293995a66e12c48d13aa1f957d09c7" + "Hash": "4b4477baa9a939c5577e5ddb4bf01f28" }, "shinycssloaders": { "Package": "shinycssloaders", @@ -1041,7 +1055,7 @@ }, "stringi": { "Package": "stringi", - "Version": "1.8.4", + "Version": "1.8.7", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1050,7 +1064,7 @@ "tools", "utils" ], - "Hash": "39e1144fd75428983dc3f63aa53dfa91" + "Hash": "2b56088e23bdd58f89aebf43a0913457" }, "stringr": { "Package": "stringr", @@ -1097,7 +1111,7 @@ }, "testthat": { "Package": "testthat", - "Version": "3.2.1.1", + "Version": "3.2.3", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1122,7 +1136,7 @@ "waldo", "withr" ], - "Hash": "3f6e7e5e2220856ff865e4834766bf2b" + "Hash": "42f889439ccb14c55fc3d75c9c755056" }, "tibble": { "Package": "tibble", @@ -1161,17 +1175,17 @@ }, "tinytex": { "Package": "tinytex", - "Version": "0.54", + "Version": "0.57", "Source": "Repository", "Repository": "RSPM", "Requirements": [ "xfun" ], - "Hash": "3ec7e3ddcacc2d34a9046941222bf94d" + "Hash": "02d65e0c0415bf36a7ddc0d2ba50a840" }, "treesitter": { "Package": "treesitter", - "Version": "0.1.0", + "Version": "0.2.0", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1181,7 +1195,7 @@ "rlang", "vctrs" ], - "Hash": "22a579e4ac2ddd021df1791da7fd080f" + "Hash": "cc0472495bc995fabbb58e2de941c384" }, "treesitter.r": { "Package": "treesitter.r", @@ -1246,7 +1260,7 @@ }, "xfun": { "Package": "xfun", - "Version": "0.49", + "Version": "0.52", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1255,11 +1269,11 @@ "stats", "tools" ], - "Hash": "8687398773806cfff9401a2feca96298" + "Hash": "652ce36fe7d57688e6786819b09d9190" }, "xml2": { "Package": "xml2", - "Version": "1.3.6", + "Version": "1.3.8", "Source": "Repository", "Repository": "RSPM", "Requirements": [ @@ -1268,7 +1282,7 @@ "methods", "rlang" ], - "Hash": "1d0336142f4cd25d8d23cd3ba7a8fb61" + "Hash": "f5130b2f3d461964bac93cc618013231" }, "xmlparsedata": { "Package": "xmlparsedata", diff --git a/tests/testthat/test-general_utils.R b/tests/testthat/test-general_utils.R index 07c9e51..a298404 100644 --- a/tests/testthat/test-general_utils.R +++ b/tests/testthat/test-general_utils.R @@ -2,7 +2,6 @@ box::use( testthat[ describe, expect_identical, - expect_error, expect_true, it ],