diff --git a/NAMESPACE b/NAMESPACE index ebc6c295..1ba60071 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -27,7 +27,6 @@ export(content_image_url) export(contents_html) export(contents_markdown) export(contents_text) -export(cortex_credentials) export(create_tool_def) export(interpolate) export(interpolate_file) diff --git a/R/provider-cortex.R b/R/provider-cortex.R index b4db2f70..dc25639a 100644 --- a/R/provider-cortex.R +++ b/R/provider-cortex.R @@ -20,13 +20,23 @@ NULL #' previous messages. Nor does it support registering tools, and attempting to #' do so will result in an error. #' +#' ## Authentication +#' +#' `chat_cortex()` picks up the following ambient Snowflake credentials: +#' +#' - A static OAuth token defined via the `SNOWFLAKE_TOKEN` environment +#' variable. +#' - Key-pair authentication credentials defined via the `SNOWFLAKE_USER` and +#' `SNOWFLAKE_PRIVATE_KEY` (which can be a PEM-encoded private key or a path +#' to one) environment variables. +#' - Posit Workbench-managed Snowflake credentials for the corresponding +#' `account`. +#' #' @param account A Snowflake [account identifier](https://docs.snowflake.com/en/user-guide/admin-account-identifier), #' e.g. `"testorg-test_account"`. #' @param credentials A list of authentication headers to pass into -#' [`httr2::req_headers()`] or a function that returns them when passed -#' `account` as a parameter. The default [`cortex_credentials()`] function -#' picks up ambient Snowflake OAuth and key-pair authentication credentials -#' and handles refreshing them automatically. +#' [`httr2::req_headers()`], a function that returns them when passed +#' `account` as a parameter, or `NULL` to use ambient credentials. #' @param model_spec A semantic model specification, or `NULL` when #' using `model_file` instead. #' @param model_file Path to a semantic model file stored in a Snowflake Stage, @@ -41,7 +51,7 @@ NULL #' chat$chat("What questions can I ask?") #' @export chat_cortex <- function(account = Sys.getenv("SNOWFLAKE_ACCOUNT"), - credentials = cortex_credentials, + credentials = NULL, model_spec = NULL, model_file = NULL, api_args = list(), @@ -56,7 +66,7 @@ chat_cortex <- function(account = Sys.getenv("SNOWFLAKE_ACCOUNT"), static_credentials <- force(credentials) credentials <- function(account) static_credentials } - check_function(credentials) + check_function(credentials, allow_null = TRUE) provider <- ProviderCortex( account = account, @@ -88,7 +98,7 @@ ProviderCortex <- new_class( }, properties = list( account = prop_string(), - credentials = class_function, + credentials = class_function | NULL, extra_args = class_list ) ) @@ -110,9 +120,8 @@ method(chat_request, ProviderCortex) <- function(provider, req <- request(provider@base_url) req <- req_url_path_append(req, "/api/v2/cortex/analyst/message") - req <- httr2::req_headers(req, - !!!provider@credentials(provider@account), .redact = "Authorization" - ) + creds <- cortex_credentials(provider@account, provider@credentials) + req <- httr2::req_headers(req, !!!creds, .redact = "Authorization") req <- req_retry(req, max_tries = 2) req <- req_timeout(req, 60) @@ -356,21 +365,13 @@ cortex_credentials_exist <- function(...) { tryCatch(is_list(cortex_credentials(...)), error = function(e) FALSE) } -#' @details -#' `cortex_credentials()` picks up the following ambient Snowflake credentials: -#' -#' - A static OAuth token defined via the `SNOWFLAKE_TOKEN` environment -#' variable. -#' - Key-pair authentication credentials defined via the `SNOWFLAKE_USER` and -#' `SNOWFLAKE_PRIVATE_KEY` (which can be a PEM-encoded private key or a path -#' to one) environment variables. -#' - Posit Workbench-managed Snowflake credentials for the corresponding -#' `account`. -#' -#' @inheritParams chat_cortex -#' @export -#' @rdname chat_cortex -cortex_credentials <- function(account = Sys.getenv("SNOWFLAKE_ACCOUNT")) { +cortex_credentials <- function(account = Sys.getenv("SNOWFLAKE_ACCOUNT"), + credentials = NULL) { + # User-supplied credentials. + if (!is.null(credentials)) { + return(credentials(account)) + } + token <- Sys.getenv("SNOWFLAKE_TOKEN") if (nchar(token) != 0) { return( diff --git a/R/provider-databricks.R b/R/provider-databricks.R index 9c5ed28b..4439c9bf 100644 --- a/R/provider-databricks.R +++ b/R/provider-databricks.R @@ -9,6 +9,8 @@ #' outputs. Tool calling support is also very limited at present; too limited #' for `elmer`'s tool calling features to work properly at all. #' +#' ## Authentication +#' #' `chat_databricks()` picks up on ambient Databricks credentials for a subset #' of the [Databricks client unified #' authentication](https://docs.databricks.com/en/dev-tools/auth/unified-auth.html) diff --git a/man/chat_cortex.Rd b/man/chat_cortex.Rd index 69e60856..f8de6b9e 100644 --- a/man/chat_cortex.Rd +++ b/man/chat_cortex.Rd @@ -2,29 +2,24 @@ % Please edit documentation in R/provider-cortex.R \name{chat_cortex} \alias{chat_cortex} -\alias{cortex_credentials} \title{Create a chatbot that speaks to the Snowflake Cortex Analyst} \usage{ chat_cortex( account = Sys.getenv("SNOWFLAKE_ACCOUNT"), - credentials = cortex_credentials, + credentials = NULL, model_spec = NULL, model_file = NULL, api_args = list(), echo = c("none", "text", "all") ) - -cortex_credentials(account = Sys.getenv("SNOWFLAKE_ACCOUNT")) } \arguments{ \item{account}{A Snowflake \href{https://docs.snowflake.com/en/user-guide/admin-account-identifier}{account identifier}, e.g. \code{"testorg-test_account"}.} \item{credentials}{A list of authentication headers to pass into -\code{\link[httr2:req_headers]{httr2::req_headers()}} or a function that returns them when passed -\code{account} as a parameter. The default \code{\link[=cortex_credentials]{cortex_credentials()}} function -picks up ambient Snowflake OAuth and key-pair authentication credentials -and handles refreshing them automatically.} +\code{\link[httr2:req_headers]{httr2::req_headers()}}, a function that returns them when passed +\code{account} as a parameter, or \code{NULL} to use ambient credentials.} \item{model_spec}{A semantic model specification, or \code{NULL} when using \code{model_file} instead.} @@ -60,9 +55,9 @@ reference to an existing file in a Snowflake Stage. Note that Cortex does not support multi-turn, so it will not remember previous messages. Nor does it support registering tools, and attempting to do so will result in an error. -} -\details{ -\code{cortex_credentials()} picks up the following ambient Snowflake credentials: +\subsection{Authentication}{ + +\code{chat_cortex()} picks up the following ambient Snowflake credentials: \itemize{ \item A static OAuth token defined via the \code{SNOWFLAKE_TOKEN} environment variable. @@ -73,6 +68,7 @@ to one) environment variables. \code{account}. } } +} \examples{ \dontshow{if (elmer:::cortex_credentials_exist()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} chat <- chat_cortex( diff --git a/man/chat_databricks.Rd b/man/chat_databricks.Rd index c9cf7fbc..a115b7d6 100644 --- a/man/chat_databricks.Rd +++ b/man/chat_databricks.Rd @@ -62,6 +62,7 @@ and can also serve as a gateway for external models hosted by a third party. Databricks models do not support images, but they do support structured outputs. Tool calling support is also very limited at present; too limited for \code{elmer}'s tool calling features to work properly at all. +\subsection{Authentication}{ \code{chat_databricks()} picks up on ambient Databricks credentials for a subset of the \href{https://docs.databricks.com/en/dev-tools/auth/unified-auth.html}{Databricks client unified authentication} @@ -74,6 +75,7 @@ model. Specifically, it supports: \item Posit Workbench-managed credentials } } +} \seealso{ Other chatbots: \code{\link{chat_bedrock}()},