## ----setup, include = FALSE---------------------------------------------------
fixture_dir <- "tidymodels"
recording <- nzchar(Sys.getenv("FOUNDRY_RECORD_DOCS"))
have_fixtures <- dir.exists(fixture_dir) && length(list.files(fixture_dir)) > 0
run_api <- requireNamespace("httptest2", quietly = TRUE) &&
  (recording || have_fixtures)
have_tidymodels <- requireNamespace("tidymodels", quietly = TRUE)

# Attach foundryR before start_vignette(): httptest2 only sources the package's
# inst/httptest2/start-vignette.R (which sets replay placeholders) from attached
# packages.
library(foundryR)

if (run_api) {
  httptest2::start_vignette(fixture_dir)
}

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = run_api && have_tidymodels
)

## ----install, eval = FALSE----------------------------------------------------
# install.packages("tidymodels")

## ----basic-recipe, eval = have_tidymodels-------------------------------------
library(tidymodels)
library(foundryR)

reviews <- tibble(
  text = c(
    "This product is useful and easy to use.",
    "The setup was confusing and slow.",
    "The examples were clear and helpful.",
    "I needed better instructions."
  ),
  sentiment = factor(c("positive", "negative", "positive", "negative"))
)


recipe_spec <- recipe(sentiment ~ text, data = reviews) %>%
  step_foundry_embed(
    text,
    model = "text-embedding-3-small",
    keep_original = FALSE
  )

recipe_spec

## ----prep-bake----------------------------------------------------------------
prepped_recipe <- prep(recipe_spec, training = reviews)

baked_data <- bake(prepped_recipe, new_data = NULL)
baked_data

## ----full-pipeline, eval = FALSE----------------------------------------------
# library(tidymodels)
# library(foundryR)
# 
# # Load your data
# set.seed(123)
# reviews <- tibble(
#   review_text = c(
#     # Positive reviews
#     "Absolutely love this product! Works perfectly.",
#     "Great quality and fast shipping. Very satisfied.",
#     "Best purchase I've made this year. Highly recommend!",
#     "Exceeded all expectations. Will buy again.",
#     "Perfect fit and great value for money.",
#     # Negative reviews
#     "Complete waste of money. Broke after one use.",
#     "Terrible customer service. Never buying again.",
#     "Poor quality, doesn't work as advertised.",
#     "Disappointed. Much smaller than expected.",
#     "Arrived damaged and took forever to ship."
#   ),
#   sentiment = factor(rep(c("positive", "negative"), each = 5))
# )
# 
# # Split data
# splits <- initial_split(reviews, prop = 0.8, strata = sentiment)
# train_data <- training(splits)
# test_data <- testing(splits)
# 
# # Define recipe with embeddings
# embedding_recipe <- recipe(sentiment ~ review_text, data = train_data) %>%
#   step_foundry_embed(
#     review_text,
#     model = "text-embedding-3-small",
#     keep_original = FALSE
#   ) %>%
#   step_normalize(all_numeric_predictors())  # Normalize embedding dimensions
# 
# # Define model
# log_reg_spec <- logistic_reg() %>%
#   set_engine("glm") %>%
#   set_mode("classification")
# 
# # Create workflow
# sentiment_workflow <- workflow() %>%
#   add_recipe(embedding_recipe) %>%
#   add_model(log_reg_spec)
# 
# # Fit the model
# fitted_workflow <- fit(sentiment_workflow, data = train_data)
# 
# # Make predictions on test data
# predictions <- predict(fitted_workflow, test_data) %>%
#   bind_cols(test_data)
# 
# # Evaluate
# predictions %>%
#   metrics(truth = sentiment, estimate = .pred_class)

## ----dimensions, eval = have_tidymodels---------------------------------------
recipe_spec <- recipe(sentiment ~ text, data = reviews) %>%
  step_foundry_embed(
    text,
    model = "text-embedding-3-small",
    dimensions = 256,  # Reduce from 1536 to 256
    keep_original = FALSE
  )

## ----multi-column, eval = have_tidymodels-------------------------------------
# Data with multiple text fields
data <- tibble(
  title = c("Great Product", "Terrible Experience"),
  description = c("Works as expected", "Broke immediately"),
  outcome = c(1, 0)
)

recipe_spec <- recipe(outcome ~ ., data = data) %>%
  step_foundry_embed(title, model = "text-embedding-3-small",
                     prefix = "title_") %>%
  step_foundry_embed(description, model = "text-embedding-3-small",
                     prefix = "desc_") %>%
  step_rm(title, description)  # Remove original text columns

## ----keep-original, eval = have_tidymodels------------------------------------
recipe_spec <- recipe(sentiment ~ text, data = reviews) %>%
  step_foundry_embed(
    text,
    model = "text-embedding-3-small",
    keep_original = TRUE  # Keep the text column
  )

# Useful when you also want to apply other text processing

## ----prefix, eval = have_tidymodels-------------------------------------------
recipe_spec <- recipe(sentiment ~ text, data = reviews) %>%
  step_foundry_embed(
    text,
    model = "text-embedding-3-small",
    prefix = "embed_"  # Columns will be embed_001, embed_002, etc.
  )

## ----precompute---------------------------------------------------------------
embedded_reviews <- foundry_embed(
  reviews$text,
  model = "text-embedding-3-small"
)

embedding_matrix <- do.call(rbind, embedded_reviews$embedding)
embedding_cols <- as_tibble(embedding_matrix, .name_repair = "unique")

precomputed <- bind_cols(
  reviews["sentiment"],
  embedding_cols
)

precomputed[, 1:4]

## ----cv, eval = FALSE---------------------------------------------------------
# # Create CV folds
# folds <- vfold_cv(train_data, v = 5, strata = sentiment)
# 
# # Fit resamples
# cv_results <- fit_resamples(
#   sentiment_workflow,
#   resamples = folds,
#   metrics = metric_set(accuracy, roc_auc)
# )
# 
# # Collect metrics
# collect_metrics(cv_results)

## ----tuning, eval = FALSE-----------------------------------------------------
# # Recipe with tunable dimensions
# tunable_recipe <- recipe(sentiment ~ text, data = train_data) %>%
#   step_foundry_embed(
#     text,
#     model = "text-embedding-3-small",
#     dimensions = tune(),  # Will be tuned
#     keep_original = FALSE
#   ) %>%
#   step_normalize(all_numeric_predictors())
# 
# # Model with tunable parameters
# rf_spec <- rand_forest(
#   mtry = tune(),
#   trees = 500,
#   min_n = tune()
# ) %>%
#   set_engine("ranger") %>%
#   set_mode("classification")
# 
# # Workflow
# tunable_workflow <- workflow() %>%
#   add_recipe(tunable_recipe) %>%
#   add_model(rf_spec)
# 
# # Define grid
# grid <- grid_regular(
#   dimensions(range = c(128, 512)),  # Embedding dimensions
#   mtry(range = c(10, 50)),
#   min_n(range = c(2, 10)),
#   levels = 3
# )
# 
# # Tune only after estimating the API calls and cost.
# tune_results <- tune_grid(
#   tunable_workflow,
#   resamples = folds,
#   grid = grid,
#   metrics = metric_set(accuracy, roc_auc)
# )
# 
# # Best parameters
# show_best(tune_results, metric = "roc_auc")

## ----memory, eval = TRUE------------------------------------------------------
# Estimate memory for 10,000 texts
n_texts <- 10000
n_dims <- 1536
bytes_per_double <- 8

memory_mb <- (n_texts * n_dims * bytes_per_double) / 1024^2
print(paste(round(memory_mb), "MB for embeddings alone"))

## ----troubleshoot, eval = have_tidymodels-------------------------------------
# Use a unique prefix if reusing recipes
recipe_spec <- recipe(sentiment ~ text, data = reviews) %>%
  step_foundry_embed(text, model = "my-model",
                     prefix = paste0("v", format(Sys.time(), "%H%M%S"), "_"))

## ----rate-limit, eval = FALSE-------------------------------------------------
# # Prepare in smaller batches
# small_sample <- reviews %>% slice_sample(n = 100)
# prepped <- prep(recipe_spec, training = small_sample)

## ----credentials, eval = FALSE------------------------------------------------
# # Check setup
# foundry_check_setup()
# 
# # Set credentials if needed.
# foundry_set_endpoint(Sys.getenv("AZURE_FOUNDRY_ENDPOINT"))
# foundry_set_key("your-api-key")

## ----cleanup, include = FALSE, eval = TRUE------------------------------------
if (run_api) {
  httptest2::end_vignette()
}

