---
title: "Getting started with foundryR"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Getting started with foundryR}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
fixture_dir <- "getting-started"
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)

# 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
)
```

## What you need from Azure

foundryR talks to deployed Azure AI Foundry and Azure OpenAI resources. Before
writing R code, create or identify:

1. An Azure OpenAI resource or Azure AI Foundry project with an OpenAI endpoint.
2. At least one chat or Responses API deployment, for example `gpt-5-nano`.
3. An embedding deployment, for example `text-embedding-3-small`, if you plan to
   use embeddings.
4. A Content Safety resource if you plan to use moderation, groundedness, or
   prompt-shield checks.
5. Either API keys or a Microsoft Entra ID token.

In the Azure portal, open your Azure OpenAI resource, then use **Keys and
Endpoint** to copy the endpoint URL and an API key. In Azure AI Foundry, use the
deployments page to create model deployments and record their deployment names.

> **Deployment name vs base model name**
>
> The value you pass to `model =` is the deployment name you chose in Azure, not
> necessarily the base model name. If you deploy base model `gpt-5-nano` with
> deployment name `my-gpt4`, use `model = "my-gpt4"` in foundryR. The same rule
> applies to embedding deployments.

## Install foundryR

```{r install, eval = FALSE}
install.packages("pak")
pak::pak("farach/foundryR")
```

## Configure credentials

Set credentials for the current R session. Credential setup is shown but not
run when building this vignette:

```{r config-session, eval = FALSE}
library(foundryR)

foundry_set_endpoint(Sys.getenv("AZURE_FOUNDRY_ENDPOINT"))
foundry_set_key("your-api-key")
```

For persistent local configuration in your own workflow, `store = TRUE` uses
the package configuration file under `tools::R_user_dir("foundryR", "config")`
unless you set the `foundryR.config_file` option. It does not modify
`.Renviron`. The file is plain text, so prefer session-only credentials or
refreshable token providers for production use.

This demonstration instead uses a temporary file and placeholder values, then
removes the file and restores the previous options and environment variables:

```{r config-persistent, eval = FALSE}
local({
  config_file <- tempfile("foundryR-config-", fileext = ".json")
  old_options <- options(foundryR.config_file = config_file)
  old_env <- Sys.getenv(
    c("AZURE_FOUNDRY_ENDPOINT", "AZURE_FOUNDRY_KEY"),
    unset = NA_character_
  )
  on.exit({
    options(old_options)
    Sys.unsetenv(names(old_env)[is.na(old_env)])
    keep <- !is.na(old_env)
    if (any(keep)) {
      do.call(Sys.setenv, as.list(old_env[keep]))
    }
    unlink(config_file)
  }, add = TRUE)

  foundry_set_endpoint("https://example.openai.azure.com", store = TRUE)
  foundry_set_key("example-key-not-a-secret", store = TRUE)
})
```

You can also edit your chosen `.Renviron` file manually with a text editor.
The vignette does not open an editor or write this file. Add values like these,
then restart R:

```text
AZURE_FOUNDRY_ENDPOINT=https://<resource-name>.openai.azure.com
AZURE_FOUNDRY_KEY=your-api-key
AZURE_FOUNDRY_MODEL=my-gpt4
AZURE_FOUNDRY_EMBED_MODEL=my-embedding-deployment
```

## Keyless authentication with Microsoft Entra ID

API keys are convenient for local testing. For enterprise environments that
already use service principals, managed identity, or Azure role-based access
control, use a Microsoft Entra ID bearer token:

```{r entra-token, eval = FALSE}
foundry_set_token("your-entra-token")
```

foundryR sends the token in the `Authorization` header. If both a token and an
API key are configured, the token takes precedence for supported calls.

## Validate setup

These checks contact your configured Azure resource and are not run during
rendering.

```{r check-setup, eval = FALSE}
foundry_check_setup()
```

Test a specific deployment:

```{r check-setup-model, eval = FALSE}
foundry_check_setup(model = "gpt-5-nano")
```

If you need to see deployments exposed by the v1 model metadata endpoint, use:

```{r models, eval = FALSE}
models <- foundry_models()
models[, c("id", "owned_by")]
```

## First Responses API call

The Responses API is the newer v1 surface for stateful turns, strict structured
outputs, tools, and richer token metadata. The examples below omit `model =`, so
foundryR reads the deployment from `AZURE_FOUNDRY_MODEL`; pass `model =` to target
a specific deployment.

```{r first-response}
library(foundryR)

response <- foundry_response("Answer in one sentence: what is R?")

response$output_text
```

Chain a follow-up turn with `previous_response_id`:

```{r response-chain}
follow_up <- foundry_response(
  "Explain why that matters for data analysis in one sentence.",
  previous_response_id = response$response_id
)

follow_up$output_text
```

## First strict extraction

Use JSON Schema when you need model output to become analyzable columns:

```{r first-extract}
schema <- list(
  type = "object",
  properties = list(
    sentiment = list(type = "string", enum = c("positive", "negative", "neutral")),
    topic = list(type = "string")
  ),
  required = c("sentiment", "topic"),
  additionalProperties = FALSE
)

foundry_extract(
  c("The tutorial was clear.", "I needed more examples."),
  schema = schema
)
```

`foundry_extract()` uses strict JSON Schema mode by default for supported models.

## First embedding

Embeddings convert text to numeric vectors for clustering, semantic search,
near-duplicate detection, and downstream models:

```{r first-embed}
texts <- c(
  "The tutorial was clear.",
  "The lecture needed more examples.",
  "The assignment instructions were easy to follow."
)

embeddings <- foundry_embed(texts, model = "text-embedding-3-small")
foundry_similarity(embeddings)
```

## Configure Content Safety

Content Safety uses a separate Azure AI Content Safety resource. In the Azure
portal, create an Azure AI Content Safety resource, open **Keys and Endpoint**,
then configure foundryR:

```{r content-safety-config, eval = FALSE}
foundry_set_content_safety_endpoint(Sys.getenv("AZURE_CONTENT_SAFETY_ENDPOINT"))
foundry_set_content_safety_key("your-content-safety-key")
```

Use groundedness and shields as auditable safety gates:

```{r safety-gate}
source <- "The program enrolled 82 students in 2026."
answer <- "The program enrolled 82 students in 2026."

grounded <- foundry_groundedness(
  text = answer,
  grounding_sources = source,
  query = "How many students enrolled?",
  task = "QnA"
)

shield <- foundry_shield(user_prompt = "Summarize this document.")

grounded
shield
```

Most foundryR calls stay within your Azure OpenAI or Content Safety resources.
Web search is different. Microsoft documents that Grounding with Bing can send
data outside the compliance and geographic boundary and can incur separate
costs. Do not send secrets or regulated data to web-search prompts.

## Chat completions

Chat completions are still available for simple assistant replies:

```{r first-chat}
foundry_chat("Answer in one sentence: what is the tidyverse?")
```

For interactive streaming chat and chat-first agent workflows, use ellmer.

## Next steps

- `vignette("foundryr-vs-ellmer")` compares foundryR with ellmer.
- `vignette("annotation-workflow")` shows extract, batch, embed, and validate.
- `vignette("responses-api")` covers Responses API tools and web search.
- `vignette("content-safety")` covers moderation, groundedness, and shields.
- `vignette("tidymodels")` covers `step_foundry_embed()`.

```{r cleanup, include = FALSE}
if (run_api) {
  httptest2::end_vignette()
}
```
